#include "stdio.h"
#include "stdlib.h"
#include "cache.h"
#include "mmu.h"
#include "v7_pmu.h"
#include "cp15.h"
#include "KeyStone_Packet_Descriptor.h"


#define PMU_COUNTER_1


/******************TCI6638 Memory Map to Region********************/
#if 1
#define START_ADDR_REGISTER1       (0x01000000)
#define NUM_SECTIONS_REGISTER1     (176)    /* First part of Register region from 0x0100_0000 - 0x0C00_0000, 176MBytes */

#define START_ADDR_MSMC            (0x0C000000)
#define NUM_SECTIONS_MSMC          (6)      /* MSMC SRAM region from 0x0C00_0000 - 0x0C60_0000, 6MBytes */

#define START_ADDR_DSP_SARM        (0x10000000)
#define NUM_SECTIONS_DSP_SARM      (128)    /* DSP0-7 L1D/P,L2 SARM region from 0x1000_0000 - 0x1800_0000, 128MBytes */

#define START_ADDR_STM_CFG         (0x20000000)
#define NUM_SECTIONS_STM_CFG       (1)      /* First part of Register region from 0x2000_0000 - 0x2010_0000, 1MBytes */

#define START_ADDR_TCP3D_DATA      (0x20600000)
#define NUM_SECTIONS_TCP3D_DATA    (4)      /* TCP3D0-3 data region from 0x2060_0000 - 0x20A0_0000, 4MBytes */

#define START_ADDR_REGISTER2       (0x21000000)
#define NUM_SECTIONS_REGISTER2     (16)     /* Second part of Register region from 0x2100_0000 - 0x2200_0000, 16MBytes */

#define START_ADDR_OTHER_DATA      (0x22A00000)
#define NUM_SECTIONS_OTHER_DATA    (982)    /* Other data region from 0x22A0_0000 - 0x6000_0000, 982MBytes */

#define START_ADDR_DDR3B           (0x60000000)
#define NUM_SECTIONS_DDR3B         (512)    /* DDR3B region from 0x6000_0000 - 0x8000_0000, 512MBytes */
#endif
#define START_ADDR_DDR3A           (0x80000000)
#define NUM_SECTIONS_DDR3A         (512)   /* DDR3A region from 0x8000_0000 - 0xA000_0000, 512MBytes */

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

static unsigned int pageTable[4*1024] __attribute((section(".TTB")));

HostPacketDescriptor *hostDesc;
unsigned int testdata[1024];
unsigned int *bufPtr;
unsigned int descLen;

#if 0
unsigned int *heap_base, *heap_end;
extern unsigned int __HeapBase;
extern unsigned int __HeapLimit;
#endif
void testcode(void)
{
	int inidex = 0;
	int i;
	int tmp;
	for (i=0;i<375;i++)
	{
		tmp = testdata[inidex];
		testdata[inidex] = 1 + tmp;
		inidex = (inidex + 1)%(1024);
	}
}

void MMUConfigAndEnable(void)
{
	unsigned int pageValue, i;

    /* Initialize the default page table and MMU */
    MMUInit((unsigned int*)pageTable);
    /***********************************DDR3A*************************************/
    /* Configure the defined regions */
    REGION regionDdr3a = {
                            MMU_PGTYPE_SECTION, START_ADDR_DDR3A, NUM_SECTIONS_DDR3A,
                            MMU_MEMTYPE_NORMAL_SHAREABLE(MMU_CACHE_WB_WA, MMU_CACHE_WB_WA),
                            MMU_REGION_NON_SECURE, MMU_AP_PRV_RW_USR_RW,
                            (unsigned int*)pageTable
                         };

    /* Map the defined regions */
    MMUMemRegionMap(&regionDdr3a);
 
    /* Mapped the Virtual address equal to the Physical address for DDR3A */
    pageValue = (regionDdr3a.startAddr & regionDdr3a.pgType) & 0xFFF00000;
    pageValue |= ((0x00040002 & regionDdr3a.pgType)
                    | regionDdr3a.accsCtrl | regionDdr3a.memAttrib
                    | regionDdr3a.secureType);

    for(i = 0; i < NUM_SECTIONS_DDR3A; i++)
    {
        pageTable[2048+i]= (pageValue + (i << 20));
    }
    
    /***********************************DDR3B*************************************/
    /* Configure the defined regions */
    REGION regionDdr3b = {
                            MMU_PGTYPE_SECTION, START_ADDR_DDR3B, NUM_SECTIONS_DDR3B,
                            MMU_MEMTYPE_NORMAL_SHAREABLE(MMU_CACHE_WB_WA, MMU_CACHE_WB_WA),
                            MMU_REGION_NON_SECURE, MMU_AP_PRV_RW_USR_RW,
                            (unsigned int*)pageTable
                         };

    /* Map the defined regions */
    MMUMemRegionMap(&regionDdr3b);

    /* Mapped the Virtual address equal to the Physical address for DDR3B */
    pageValue = (regionDdr3b.startAddr & regionDdr3b.pgType) & 0xFFF00000;
    pageValue |= ((0x00040002 & regionDdr3b.pgType)
                    | regionDdr3b.accsCtrl | regionDdr3b.memAttrib
                    | regionDdr3b.secureType);

    for(i = 0; i < NUM_SECTIONS_DDR3B; i++)
    {
        pageTable[1536+i]= (pageValue + (i << 20));
    }

    /***********************************MSMC*************************************/
    /* Configure the defined regions */
    REGION regionMsmc = {
                 MMU_PGTYPE_SECTION, START_ADDR_MSMC, NUM_SECTIONS_MSMC,
                 MMU_MEMTYPE_NORMAL_SHAREABLE(MMU_CACHE_WB_WA, MMU_CACHE_WB_WA),
                 MMU_REGION_NON_SECURE, MMU_AP_PRV_RW_USR_RW,
                 (unsigned int*)pageTable
                };

    /* Map the defined regions */
    MMUMemRegionMap(&regionMsmc);

    /* Mapped the Virtual address equal to the Physical address for MSMC */
    pageValue = (regionMsmc.startAddr & regionMsmc.pgType) & 0xFFF00000;
    pageValue |= ((0x00040002 & regionMsmc.pgType)
                    | regionMsmc.accsCtrl | regionMsmc.memAttrib
                    | regionMsmc.secureType);

    for(i = 0; i < NUM_SECTIONS_MSMC; i++)
    {
        pageTable[192+i]= (pageValue + (i << 20));
    }

    /***********************************REGISTER1*************************************/
    /* Configure the defined regions */
    REGION regionRegister1 = {
                 MMU_PGTYPE_SECTION, START_ADDR_REGISTER1, NUM_SECTIONS_REGISTER1,
                 MMU_MEMTYPE_DEVICE_SHAREABLE,
                 MMU_REGION_NON_SECURE, MMU_AP_PRV_RW_USR_RW,
                 (unsigned int*)pageTable
                };

    /* Map the defined regions */
    MMUMemRegionMap(&regionRegister1);

    /* Mapped the Virtual address equal to the Physical address for REGISTER1 */
    pageValue = (regionRegister1.startAddr & regionRegister1.pgType) & 0xFFF00000;
    pageValue |= ((0x00040002 & regionRegister1.pgType)
                    | regionRegister1.accsCtrl | regionRegister1.memAttrib
                    | regionRegister1.secureType);

    for(i = 0; i < NUM_SECTIONS_REGISTER1; i++)
    {
        pageTable[16+i]= (pageValue + (i << 20));
    }

    /* Now Safe to enable MMU */
    MMUEnable((unsigned int*)pageTable);

}


int testcycle[10];
int testcycle_tmp;

/*core 0*/
void main()
{
	unsigned int i;
	unsigned int MMUtestflag = 0;

#if 0
	heap_base = &__HeapBase;
	heap_end  = &__HeapLimit;

	printf("Heap base: 0x%08X, heap end: 0x%08X.\n", heap_base, heap_end);
#endif

    /* Configure and enable the MMU */
    MMUConfigAndEnable();
    printf("MMU is enabled\n");

    /* Enable all levels of CACHE */
    CacheEnable(CACHE_ALL);

    /* Enable Brabch prediction */
    CP15BranchPredictionEnable();

    asm("    DSB");
   
    /* Enable PMU and relative cycle counter registers */
    _enable_pmu();
    _enable_ccnt();
    _reset_ccnt();

    printf("------------ Non-cache VS Cache Cycle Test Begin ----------------\n");
    /* Test for non-cache scenario and get the cycle count */
    CacheDisable(CACHE_DCACHE);
    testcycle_tmp = _read_ccnt();
    testcode();
    testcycle[0] = _read_ccnt() - testcycle_tmp;
    printf("non-Dcacheable cycle is %u\n", testcycle[0]);

    /*Enable data cache and test*/
    CacheEnable(CACHE_DCACHE);
    CacheInstInvalidateAll();
    CacheDataCleanInvalidateAll();

    asm("    DSB");

    /* Configure PMU to monitor relative events */
#ifdef PMU_COUNTER_1
    _pmn_config(0, 0x04);       // Configure counter 0 to count L1D-Cache Accesses
    _pmn_config(1, 0x16);       // Configure counter 1 to count L2D-Cache Access
    _pmn_config(2, 0x42);       // Configure counter 2 to count L1D-Cache Read Misses
    _pmn_config(3, 0x43);       // Configure counter 3 to count L1D-Cache Write Misses
    _pmn_config(4, 0x52);       // Configure counter 4 to count L2D-Cache Read Misses
    _pmn_config(5, 0x53);       // Configure counter 5 to count L2D-Cache write Misses
#endif

#ifdef PMU_COUNTER_2
    _pmn_config(0, 0x08);       // Configure counter 0 to executed instruction
    _pmn_config(1, 0x0C);       // Configure counter 1 to software PC change
    _pmn_config(2, 0x10);       // Configure counter 2 to mis/not predicted branch
    _pmn_config(3, 0x12);       // Configure counter 3 to could predicted branch
    _pmn_config(4, 0x1B);       // Configure counter 4 to count instruction speculatively executed
    _pmn_config(5, 0x6A);       // Configure counter 5 to count
#endif

    /* Clean previous status and enable corresponding counters */
    _reset_pmn();               // Reset the configurable counters
    _enable_pmn(0);             // Enable counter 0
    _enable_pmn(1);             // Enable counter 1
    _enable_pmn(2);             // Enable counter 2
    _enable_pmn(3);             // Enable counter 3
    _enable_pmn(4);             // Enable counter 4
    _enable_pmn(5);             // Enable counter 5
    _reset_ccnt();

    /* Test for cache scenario and get the cycle count */
    testcycle_tmp = _read_ccnt();
    testcode();
    testcycle[1] = _read_ccnt() - testcycle_tmp;

    /* Stop PMN counters */
    _disable_pmn(0);            // Stop counter 0
    _disable_pmn(1);            // Stop counter 1
    _disable_pmn(2);            // Stop counter 2
    _disable_pmn(3);            // Stop counter 3
    _disable_pmn(4);            // Stop counter 4
    _disable_pmn(5);            // Stop counter 5

#ifdef PMU_COUNTER_1
    printf("L1D-Cache Accesses = %u\n", _read_pmn(0));
    printf("L2D-Cache Access = %u\n", _read_pmn(1));
    printf("L1D-Cache Read Misses = %u\n", _read_pmn(2));
    printf("L1D-Cache Write Misses = %u\n", _read_pmn(3));
    printf("L2D-Cache Read Misses = %u\n", _read_pmn(4));
    printf("L2D-Cache write Misses = %u\n", _read_pmn(5));
#endif

#ifdef PMU_COUNTER_2
    printf("executed instruction = %u\n", _read_pmn(0));
    printf("software PC change = %u\n", _read_pmn(1));
    printf("mis/not predicted branch = %u\n", _read_pmn(2));
    printf("could predicted branch = %u\n", _read_pmn(3));
    printf("instruction speculatively executed = %u\n", _read_pmn(4));
    printf("unaligned access = %u\n", _read_pmn(5));
#endif

    printf("L1+L2 cacheable cycle is %u\n", testcycle[1]);

    printf("------------ Non-cache VS Cache Cycle Test End ----------------\n");

    hostDesc = (HostPacketDescriptor*)((*(unsigned int *)(0x02A80000+16*2004+0xC)) & 0xFFFFFFF0);  //RegD of SrcQue 2004
    descLen = (*(unsigned int *)(0x02A80000+16*2004+0xC)) & 0x0000000F;

    printf("Host descriptor address is 0x%08X \n", hostDesc);

    CacheDataInvalidateBuff((unsigned int)hostDesc, 32);
    asm("    DSB");

    bufPtr = (unsigned int *)hostDesc->buffer_ptr;
    printf("Host descriptor buffer ptr is 0x%08X \n", bufPtr);

    for(i=0; i<6; i++)
    {
    	bufPtr[i] = _read_pmn(i);
    }
    bufPtr[i] = testcycle[1];
    CacheDataCleanBuff((unsigned int)bufPtr, 7*4);
    asm("    DSB");

    hostDesc->buffer_len = 7*4;
    hostDesc->packet_length = 7*4;
    hostDesc->src_tag_lo = 1;

    CacheDataCleanBuff((unsigned int)hostDesc, 32);
    asm("    DSB");

    *(unsigned int *)(0x02A80000+16*800+0xC) = ((unsigned int)hostDesc)|descLen; // RegD of TxQue 800

    printf("------------ Send test result from ARM to DSP ----------------\n");
    
    printf("ARM code exit!\n");

    return;
}
