#include "m2sxxx.h"
#include "drivers/mss_uart/mss_uart.h"

/**************************************************************************/
/*Extern Declarations*/
/**************************************************************************/
void remap_user_code_eSRAM_0(void);
void remap_user_code_eSRAM_1(void);
void remap_user_code_eNVM_128KB(void);

#define ESRAM0_REMAP           0
#define ESRAM1_REMAP           1
#define ESRAM2_REMAP           2
#define ESRAM3_REMAP           3

#define ESRAM0_REMAP_BASE_ADDR 0x20000000
#define ESRAM1_REMAP_BASE_ADDR 0x20008000

#define ENVM_DEMO_IMAGE_STORED_0 0x60004000
#define ENVM_DEMO_IMAGE_STORED_1 0x60008000
#define ENVM_DEMO_IMAGE_STORED_2 0x60012000
#define ENVM_DEMO_IMAGE_STORED_3 0x60016000

void copy_image_to_ram(int type)
{
    unsigned int ii=0;
    unsigned long *exeDestAddr, *exeSrcAddr;

    switch(type)
    {
		case ESRAM0_REMAP:
		{
			exeDestAddr = (unsigned long *)ESRAM0_REMAP_BASE_ADDR;
			exeSrcAddr = (unsigned long *)ENVM_DEMO_IMAGE_STORED_0;
			/* 16 K B = 16 KB /4 ptr increments by 4bytes*/
			for (ii=0; ii<4096; ii++ )
			{
				*exeDestAddr++ = *exeSrcAddr++;
			}
		}
		break;
		case ESRAM1_REMAP:
		{
			exeDestAddr = (unsigned long *)ESRAM1_REMAP_BASE_ADDR;
			exeSrcAddr = (unsigned long *)ENVM_DEMO_IMAGE_STORED_1;
			/* 16 K B = 16 KB /4 ptr increments by 4bytes*/
			for (ii=0; ii<4096; ii++ )
			{
				*exeDestAddr++ = *exeSrcAddr++;
			}
		}
		break;
		case ESRAM2_REMAP:
		{
			exeDestAddr = (unsigned long *)ESRAM0_REMAP_BASE_ADDR;
			exeSrcAddr = (unsigned long *)ENVM_DEMO_IMAGE_STORED_2;
			/* 16 K B = 16 KB /4 ptr increments by 4bytes*/
			for (ii=0; ii<4096; ii++ )
			{
				*exeDestAddr++ = *exeSrcAddr++;
			}
		}
		break;
		case ESRAM3_REMAP:
		{
			exeDestAddr = (unsigned long *)ESRAM1_REMAP_BASE_ADDR;
			exeSrcAddr = (unsigned long *)ENVM_DEMO_IMAGE_STORED_3;
			/* 16 K B = 16 KB /4 ptr increments by 4bytes*/
			for (ii=0; ii<4096; ii++ )
			{
				*exeDestAddr++ = *exeSrcAddr++;
			}
		}
		break;
		default:
			break;
    }
}

size_t
UART_Polled_Rx
(
    mss_uart_instance_t * this_uart,
    uint8_t * rx_buff,
    size_t buff_size
)
{
    size_t rx_size = 0U;

    while( rx_size < buff_size )
    {
       while ( ((this_uart->hw_reg->LSR) & 0x1) != 0U  )
       {
           rx_buff[rx_size] = this_uart->hw_reg->RBR;
           ++rx_size;
       }
    }

    return rx_size;
}

/****************************************************************/
/* Entry to Main form user bootcode                             */
/****************************************************************/
int main()
{
    uint8_t rx_buff[1];
	/* Initialization all necessary hardware components */
    MSS_UART_init( &g_mss_uart1,MSS_UART_57600_BAUD,MSS_UART_DATA_8_BITS | MSS_UART_NO_PARITY | MSS_UART_ONE_STOP_BIT );
    MSS_UART_polled_tx(&g_mss_uart1,(uint8_t *)"#Welcome to SmartFusion2 - Running HexFile0#\n\r",sizeof("#Welcome to SmartFusion2 - Running HexFile0#\n\r"));

  	while(1)
  	{
  	    MSS_UART_polled_tx(&g_mss_uart1,(uint8_t *)"Select the below option\n\r",sizeof("Select the below option\n\r"));
  	    MSS_UART_polled_tx(&g_mss_uart1,(uint8_t *)"1. Run Hex File1 from eSRAM\n\r",sizeof("1. Run Hex File1 from eSRAM\n\r"));
  	    MSS_UART_polled_tx(&g_mss_uart1,(uint8_t *)"2. Run Hex File2 from eSRAM\n\r",sizeof("2. Run Hex File2 from eSRAM\n\r"));
  	    MSS_UART_polled_tx(&g_mss_uart1,(uint8_t *)"3. Run Hex File3 from eSRAM\n\r",sizeof("3. Run Hex File3 from eSRAM\n\r"));
  	    MSS_UART_polled_tx(&g_mss_uart1,(uint8_t *)"4. Run Hex File4 from eSRAM\n\r",sizeof("4. Run Hex File4 from eSRAM\n\r"));
  	    MSS_UART_polled_tx(&g_mss_uart1,(uint8_t *)"5. Run Hex File5 from eNVM\n\r", sizeof("5. Run Hex File5 from eNVM\n\r"));

  		while( (!(UART_Polled_Rx ( &g_mss_uart1, rx_buff, 1 ))) )
			;

  		if(rx_buff[0] == '1')
	    {
	  	    MSS_UART_polled_tx(&g_mss_uart1,(uint8_t *)"\n\rRunning Hex File1\n\r",sizeof("\n\rRunning Hex File1\n\r"));
	    	copy_image_to_ram(ESRAM0_REMAP);
	    	remap_user_code_eSRAM_0();
	    }
	    else if(rx_buff[0] == '2')
	    {
	    	MSS_UART_polled_tx(&g_mss_uart1,(uint8_t *)"\n\rRunning Hex File2\n\r",sizeof("\n\rRunning Hex File2\n\r"));
	    	copy_image_to_ram(ESRAM1_REMAP);
	    	remap_user_code_eSRAM_1();
	    }
	    else if(rx_buff[0] == '3')
	    {
	    	MSS_UART_polled_tx(&g_mss_uart1,(uint8_t *)"\n\rRunning Hex File3\n\r",sizeof("\n\rRunning Hex File3\n\r"));
	    	copy_image_to_ram(ESRAM2_REMAP);
	    	remap_user_code_eSRAM_0();
	    }else if(rx_buff[0] == '4')
	    {
	    	MSS_UART_polled_tx(&g_mss_uart1,(uint8_t *)"\n\rRunning Hex File4\n\r",sizeof("\n\rRunning Hex File4\n\r"));
	        copy_image_to_ram(ESRAM3_REMAP);
	    	remap_user_code_eSRAM_1();
	    }
	    else if(rx_buff[0] == '5')
	    {
	  	    MSS_UART_polled_tx(&g_mss_uart1,(uint8_t *)"\n\rRunning Hex File5\n\r",sizeof("\n\rRunning Hex File5\n\r"));
	    	remap_user_code_eNVM_128KB();
	    }
	    else
	    {
	  	    MSS_UART_polled_tx(&g_mss_uart1,(uint8_t *)"Incorrect option\n\r",sizeof("Incorrect option\n\r"));
	    }
  	}

    return 0;
}

void remap_user_code_eNVM_128KB(void)
{
	int * address = (int *)0x00000004; 		//pointer to reset handler of application
    __set_MSP(*(int*)0x00000000); 			//set the stack pointer to that of the application
    SYSREG->ENVM_CR &= 0xFFFFFFF0;
    SYSREG->ENVM_CR |= 0x10;
    SYSREG->ENVM_REMAP_BASE_CR = 0x20001;
    ((void (*)())(*address))(); 				// pointer recast as function pointer and the dereferenced/called
    while(1)
    {
    };						    //This instruction never executed
}

void remap_user_code_eSRAM_0(void)
{
   int * address = (int *)0x20000004; 		//pointer to reset handler of application
   __set_MSP(*(int*)0x20000000); 			//set the stack pointer to that of the application
   SYSREG->ESRAM_CR |= 0x1;
   ((void (*)())(*address))(); 				// pointer recast as function pointer and the dereferenced/called
   while(1){ }; 						    //This instruction never executed
}


void remap_user_code_eSRAM_1()
{
    int * address = (int *)0x20008004; 		//pointer to reset handler of application
   __set_MSP(*(int*)0x20008000); 			//set the stack pointer to that of the application
   SYSREG->ESRAM_CR |= 0x3;
   ((void (*)())(*address))(); 				// pointer recast as function pointer and the dereferenced/called
   while(1){ }; 						    //This instruction never executed
}
