/*
Copyright 2018 Embedded Microprocessor Benchmark Consortium (EEMBC)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Original Author: Shay Gal-on
*/
/*
 * Copyright (c) 2017 - 2018 , NXP
 * All rights reserved.
 */

#include <stdio.h>
#include <stdlib.h>
#include "coremark.h"

/* added for LPC55S1x platform */
#include "fsl_common.h"
#include "fsl_clock.h"
#include "fsl_gpio.h"
#include "fsl_iocon.h"
#include "fsl_usart.h"
#include "fsl_power.h"
#include "clock_config.h"
#include <stdbool.h>

/* User should consider ITERATIONS values */
#define ITERATIONS 4000

#define RESULT_OUTPUT 1

#if VALIDATION_RUN
  volatile ee_s32 seed1_volatile=0x3415;
  volatile ee_s32 seed2_volatile=0x3415;
  volatile ee_s32 seed3_volatile=0x66;
#endif
#if PERFORMANCE_RUN
  volatile ee_s32 seed1_volatile=0x0;
  volatile ee_s32 seed2_volatile=0x0;
  volatile ee_s32 seed3_volatile=0x66;
#endif
#if PROFILE_RUN
  volatile ee_s32 seed1_volatile=0x8;
  volatile ee_s32 seed2_volatile=0x8;
  volatile ee_s32 seed3_volatile=0x8;
#endif
  volatile ee_s32 seed4_volatile=ITERATIONS;
  volatile ee_s32 seed5_volatile=0;
  
/* Porting : Timing functions
   How to capture time and convert to seconds must be ported to whatever is supported by the platform.
   e.g. Read value from on board RTC, read value from cpu clock cycles performance counter etc. 
   Sample implementation for standard time.h and windows.h definitions included.
*/
/* Define : TIMER_RES_DIVIDER
   Divider to trade off timer resolution and total time that can be measured.

   Use lower values to increase resolution, but make sure that overflow does not occur.
   If there are issues with the return value overflowing, increase this value.
*/
#define EE_TICKS_PER_SEC 1000
volatile uint32_t gTicks; /* Systick counter var. */
#define SysTick_Counter_Disable   ((uint32_t)0xFFFFFFFE)
#define SysTick_Counter_Enable    ((uint32_t)0x00000001)
#define SysTick_Counter_Clear     ((uint32_t)0x00000000)

/* Function : start_time
   This function will be called right before starting the timed portion of the benchmark.

   Implementation may be capturing a system timer (as implemented in the example code) 
   or zeroing some system parameters - e.g. setting the cpu clocks cycles to 0.
*/
void start_time(void) {
#if RESULT_OUTPUT
    gTicks = 0;
    /* Enable SystemTick every 1mS */
    SysTick_Config(SystemCoreClock/1000);  
#endif
}

/* Function : stop_time
   This function will be called right after ending the timed portion of the benchmark.

   Implementation may be capturing a system timer (as implemented in the example code) 
   or other system parameters - e.g. reading the current value of cpu cycles counter.
*/
void stop_time(void) {
#if RESULT_OUTPUT
    SysTick->CTRL &= SysTick_Counter_Disable; /* Stop the Timer and get the encoding time */
    SysTick->VAL   = SysTick_Counter_Clear; /* Clear the SysTick Counter */ 
#endif
}
/* Function : get_time
   Return an abstract "ticks" number that signifies time on the system.
   Actual value returned may be cpu cycles, milliseconds or any other value,
   as long as it can be converted to seconds by <time_in_secs>.
   This methodology is taken to accomodate any hardware or simulated platform.
   The sample implementation returns millisecs by default, 
   and the resolution is controlled by <TIMER_RES_DIVIDER>
*/
CORE_TICKS get_time(void) {
    CORE_TICKS elapsed = (CORE_TICKS)gTicks;
    return elapsed;
}
/* Function : time_in_secs
   Convert the value returned by get_time to seconds.

   The <secs_ret> type is used to accomodate systems with no support for floating point.
   Default implementation implemented by the EE_TICKS_PER_SEC macro above.
*/
secs_ret time_in_secs(CORE_TICKS ticks) {
    secs_ret retval=((secs_ret)ticks) / (secs_ret)EE_TICKS_PER_SEC;
    return retval;
}

ee_u32 default_num_contexts=1;

/* Function : portable_init
   Target specific initialization code 
   Test for some common mistakes.
*/
void portable_init(core_portable *p, int *argc, char *argv[])
{
    /* added intialize function here for LPC5500 */
    usart_config_t config;
    volatile uint8_t s_CmdRecv = 0;
    /* Board pin init */
    /* Enables the clock for the IOCON block. 0 = Disable; 1 = Enable.: 0x01u */
    CLOCK_EnableClock(kCLOCK_Iocon);
    /* Debug Usart Pin Init */
    IOCON->PIO[0][30] = (IOCON_FUNC1 | IOCON_MODE_INACT  | IOCON_DIGITAL_EN);  // Debug Usart TXD pin
    IOCON->PIO[0][29] = (IOCON_FUNC1 | IOCON_MODE_INACT  | IOCON_DIGITAL_EN);  // Debug Usart TXD pin
    CLOCK_DisableClock(kCLOCK_Iocon);
    /* attach 12 MHz clock to FLEXCOMM0 (debug console) */
    CLOCK_AttachClk(kFRO12M_to_FLEXCOMM0);
    /*
     * config.baudRate_Bps = 115200U;
     * config.parityMode = kUSART_ParityDisabled;
     * config.stopBitCount = kUSART_OneStopBit;
     * config.loopback = false;
     * config.enableTx = false;
     * config.enableRx = false;
     */
    USART_GetDefaultConfig(&config);
    config.baudRate_Bps = 115200;
    config.enableTx = true;
    config.enableRx = true;
    USART_Init(USART0, &config, 12000000);
    
    ee_printf("\r\nLPC55S0x CoreMark Test Program Start\r\n");
    ee_printf("Please Select Core Freqency first by input 1/2/3\r\n");
    ee_printf("1 - FRO 12MHz\r\n");
    ee_printf("2 - FRO 96MHz\r\n");
    ee_printf("3 - PLL 96MHz\r\n");
    USART_ReadBlocking(USART0, (uint8_t *)&s_CmdRecv, 1);

    /* Set Main Freq as 12MHz at first*/
    BOARD_BootClockFRO12M();
    if(s_CmdRecv == '1')
    {
        BOARD_BootClockFRO12M();     /* Set Main Freq as FRO 12MHz */
        seed4_volatile = 2000;
    }
    if(s_CmdRecv == '2')
    {
        BOARD_BootClockFROHF96M();   /* Set Main Freq as FRO 96MHz */
        seed4_volatile = 6000;
    }
    if(s_CmdRecv == '3')
    {
        BOARD_BootClockPLL96M();     /* Set Main Freq as PLL 96MHz */
        seed4_volatile = 6000;
    } 

#ifdef RUN_ON_FLASH
    /* Enable Prefetch & Override */
    SYSCON->FMCCR |= (1 << 5);
#endif

    /* Update System Frequency Value */
    SystemCoreClockUpdate();

    /* Print main clock frequency information */ 
    ee_printf("SystemCoreClock: %d\n", SystemCoreClock);
    
#ifdef RUN_ON_RAMX
    ee_printf("System Running on SRAM-X\n");
#endif
#ifdef RUN_ON_FLASH
    ee_printf("System Running on FLASH\n");
#endif

#ifndef COREMARK_SCORE_TEST
    /* Enable Automatic Clock Gating for all modules */
    SYSCON->AUTOCLKGATEOVERRIDE = 0xC0DE0000; 
    /* Enable clocks only for : ROM, all SRAM_CTRL, Flexcomm0 and Analog Controller. All others modules clocks are disabled */
#ifdef RUN_ON_RAMX
    SYSCON->AHBCLKCTRLX[0] = 0x000001F8;    /* SRAM_CTRL1,2,3,4  */
    SYSCON->AHBCLKCTRLX[1] = 0x00000000;    /* Flexcomm 0 */
    SYSCON->AHBCLKCTRLX[2] = 0x00000000;    /* Analog Controller */

    //ANACTRL->BOD_DCDC_INT_CTRL = 0x00;
    //PMC->RESETCTRL = 0x00;
    //PMC->PDRUNCFGSET0 = (1 << 22) | (1 << 3) | (1 << 2);
#endif

#ifdef RUN_ON_FLASH
    SYSCON->AHBCLKCTRLX[0] = 0x000001F8;    /* SRAM_CTRL1,2,3,4, FMC  */
    SYSCON->AHBCLKCTRLX[1] = 0x00000000;    /* Flexcomm 0 */
    SYSCON->AHBCLKCTRLX[2] = 0x00000000;    /* Analog Controller */
    //ANACTRL->BOD_DCDC_INT_CTRL = 0x00;
    //PMC->RESETCTRL = 0x00;
    //PMC->PDRUNCFGSET0 = (1 << 3);
#endif

#endif
    if (sizeof(ee_ptr_int) != sizeof(ee_u8 *)) {
        ee_printf("ERROR! Please define ee_ptr_int to a type that holds a pointer!\n");
    }
    if (sizeof(ee_u32) != 4) {
        ee_printf("ERROR! Please define ee_u32 to a 32b unsigned type!\n");
    }
    p->portable_id=1;

#ifndef COREMARK_SCORE_TEST
    // Measurement Core average current need disable UART and restore IO Pin Status
    IOCON->PIO[0][30] &= 0xFFFFF800;
    IOCON->PIO[0][29] &= 0xFFFFF800;
    /* De-attach clock to FLEXCOMM0 */
    USART0->CFG &= ~(USART_CFG_ENABLE_MASK);
    CLOCK_AttachClk(kNONE_to_FLEXCOMM0);
#endif

#ifdef RUN_ON_RAMX
    /* Re-allicate interrput VTOR table to SRAM-0 */
    memcpy((uint32_t*)0x20000000, (uint32_t*)SCB->VTOR, 0x140);
    SCB->VTOR = (uint32_t)0x20000000;
    /* Power Down the Flash */
    FLASH->CMD = 0x1;
#endif
}

/* Function : portable_fini
	Target specific final code 
*/
void portable_fini(core_portable *p)
{
    p->portable_id=0;
    volatile uint8_t s_CmdRecv;
#ifndef COREMARK_SCORE_TEST
    usart_config_t config;

    /* Board pin init */
    /* Enables the clock for the IOCON block. 0 = Disable; 1 = Enable.: 0x01u */
    CLOCK_EnableClock(kCLOCK_Iocon);
    /* Debug Usart Pin Init */
    IOCON->PIO[0][30] = (IOCON_FUNC1 | IOCON_MODE_INACT  | IOCON_DIGITAL_EN);  // Debug Usart TXD pin
    IOCON->PIO[0][29] = (IOCON_FUNC1 | IOCON_MODE_INACT  | IOCON_DIGITAL_EN);  // Debug Usart TXD pin
    CLOCK_DisableClock(kCLOCK_Iocon);
    /* attach 12 MHz clock to FLEXCOMM0 (debug console) */
    CLOCK_AttachClk(kFRO12M_to_FLEXCOMM0);
    /*
     * config.baudRate_Bps = 115200U;
     * config.parityMode = kUSART_ParityDisabled;
     * config.stopBitCount = kUSART_OneStopBit;
     * config.loopback = false;
     * config.enableTx = false;
     * config.enableRx = false;
     */
    USART_GetDefaultConfig(&config);
    config.baudRate_Bps = 115200;
    config.enableTx = true;
    config.enableRx = true;
    USART_Init(USART0, &config, 12000000);
#endif
    ee_printf("Test DONE, Press anykey to start again\r\n");
    USART_ReadBlocking(USART0, (uint8_t *)&s_CmdRecv, 1);
    NVIC_SystemReset();     /* Reboot System */

    while(1)
    {
        __WFI();
    }
}

/* Systick ISR. */
void SysTick_Handler(void)
{
    gTicks++;
}
