/*******************************************************************************
 * (c) Copyright 2016-2017 Microsemi SoC Products Group. All rights reserved.
 *
 *  Simple boot-loader example program.
 *  This sample project is targeted at a RISC-V design running on the M2S150
 *  development board.
 *  You can program the SPI Flash from a command line program and have the
 *  boot-loader load a program from SPI Flash and jump to it.
 *  These actions are driven from a serial command line interface.
 *
 * SVN $Revision: $
 * SVN $Date: $
 */
#include <unistd.h>

#include "hw_platform.h"
//#include "riscv_hal.h"
#include "miv_rv32_hal.h"


#include "drivers/CoreGPIO/core_gpio.h"
#include "drivers/CoreUARTapb/core_uart_apb.h"
#include "drivers/CoreSPI/core_spi.h"

#include <string.h>
#include "mt25ql01gbbb.h"

#define FLASH_SECTOR_SIZE   65536 // Sectors are 64K bytes
#define FLASH_SECTORS       128   // This is an 8MB part with 128 sectors of 64KB
#define FLASH_BLOCK_SIZE    4096  // We will use the 4K blocks for this example
#define FLASH_SEGMENT_SIZE  256   // Write segment size

#define FLASH_BLOCK_SEGMENTS ( FLASH_BLOCK_SIZE / FLASH_SEGMENT_SIZE )

#define FLASH_BYTE_SIZE		(FLASH_SECTOR_SIZE * FLASH_SECTORS)
#define LAST_BLOCK_ADDR     (FLASH_BYTE_SIZE - FLASH_BLOCK_SIZE)
#define F_P 1
#define DDR_APP_MAX_SIZE 1024*1024*1 //1MB
static void Bootloader_JumpToApplication(uint32_t stack_location, uint32_t reset_vector);


/*
 * Data structure stored at the beginning of SPI flash to indicate the suize of
 * data stored inside the SPI flash. This is to avoid having to read the entire
 * flash content at boot time.
 * This data structure is one flash segment long (256 bytes).
 */
typedef struct
{
    uint32_t validity_key;
    uint32_t spi_content_byte_size;
    uint32_t dummy[62];
} flash_content_t;

/*
 * Base address of DDR memory where executable will be loaded.
 */
#define DDR_BASE_ADDRESS    0x80200000

/*
 * Delay loop down counter load value.
 */
#define DELAY_LOAD_VALUE     0x00008000

/*
 * Bit mask identifying the DIP switch used to indicate whether the boot loader
 * should load and launch the application on system reset or stay running to
 * allow a new image to be programming into the SPI flash.
 */
#define BOOTLOADER_DIP_SWITCH   0x00000080

/*
 * Key value used to determine if valid data is contained in the SPI flash.
 */
#define SPI_FLASH_VALID_CONTENT_KEY     0xB5006BB1

/*
 * CoreGPIO instance data.
 */
gpio_instance_t g_gpio;

volatile uint32_t g_10ms_count;
volatile uint32_t g_state;

/////////////////
#define BUFFER_SIZE 4096

void delay1(volatile uint32_t n)
{
	while(n)
		n--;
}
//extern void delay(volatile int i);
//*&************************************************************
uint8_t g_write_buffer[BUFFER_SIZE];
uint8_t no_of_files =0;
uint32_t flash_address[3] = {0x1000,0xA00000,0x1400000};
uint8_t g_read_buf[BUFFER_SIZE];
uint32_t g_src_image_target_address =0;
uint32_t g_file_size = 0;
uint32_t g_flash_address = 0;
#define MAX_FILES 1
///////////////
/******************************************************************************
 * Maximum receiver buffer size.
 *****************************************************************************/
#define MAX_RX_DATA_SIZE    256

/******************************************************************************
 * CoreUARTapb instance data.
 *****************************************************************************/
UART_instance_t g_uart;

/******************************************************************************
 * Instruction message. This message will be transmitted over the UART to
 * HyperTerminal when the program starts.
 *****************************************************************************/
const uint8_t g_greeting_msg[] =
"\r\n\r\n\
===============================================================================\r\n\
                    Microsemi Mi-V Boot Loader v1.0.0\r\n\
===============================================================================\r\n\
 This boot loader provides the following features:\r\n\
    - Load a program into SPI Flash memory using UART.\r\n\
    - Load a program from SPI flash into external DDR memory \r\n\
    - Launch the loaded program from DDR.\r\n\
 ";

const uint8_t g_instructions_msg[] =
"\r\n\r\n\
-------------------------------------------------------------------------------\r\n\
 Options:\n\r\
 Type 0 to program flash with application image \n\r\
 Type 1 to copy program from flash to DDR \n\r\
 Type 2 to start program loaded in DDR \n\r\
 Type 3 to copy program from flash to DDR and launch the program from DDR \n\r\
";





const uint8_t g_load_executable_msg[] =
"\r\n\
-------------------------------------------------------------------------------\r\n\
 Loading application from SPI flash into DDR memory.\r\n";

const uint8_t g_run_executable_msg[] =
"\r\n\
-------------------------------------------------------------------------------\r\n\
 Executing application in DDR memory.\r\n\
-------------------------------------------------------------------------------\r\n\
 \r\n";


/******************************************************************************
 * GPIO instance data.
 *****************************************************************************/
gpio_instance_t g_gpio;

/*-------------------------------------------------------------------------*//**
 * main() function.
 */
#if F_P
static uint32_t read_page_from_host_through_uart
(
		uint8_t * g_buffer,
		uint32_t length
)
{
	uint32_t num_bytes,factor,temp;
	volatile uint32_t i = 0;
	num_bytes = length;
	char crc;
	size_t rx_size = 0;


	uint8_t rx_buff[1],temp_add[2];
	//Write Ack "b" to indicate beginning of the transaction from the target

	if(g_src_image_target_address + length > g_file_size )
	{
		num_bytes = g_file_size - g_src_image_target_address;
	}
	if(g_src_image_target_address>= g_file_size)
	{
		return 0;
	}
	CRCFAIL:


	UART_send(&g_uart, (const uint8_t * )"b",1);
	//poll for Ack message from the host as an acknowledgment that the host is ready for receiving the transaction

	while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
		;

#if 1
	temp = g_src_image_target_address/4096;
	temp_add[0] = temp&0xFF;
	temp_add[1] = (temp>>8)&0xFF;
	if(rx_buff[0]== 'a')
	{
		UART_send(&g_uart,&temp_add[0],1);
		for(i=0;i<500;i++);
		while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
			;

		if(rx_buff[0]== 'a')
			UART_send(&g_uart,&temp_add[1],1);
		for(i=0;i<500;i++);

	}


	while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
		;
#endif

	temp_add[0] = num_bytes&0xFF;
	temp_add[1] = (num_bytes>>8)&0xFF;
	if(rx_buff[0]== 'a')
	{

		UART_send(&g_uart,&temp_add[0],1);
		while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
			;
		for(i=0;i<500;i++);
		if(rx_buff[0]== 'a')
			UART_send(&g_uart,&temp_add[1],1);
		for(i=0;i<500;i++);
	}

	//poll for Ack message from the host as an acknowledgment that the host received the return bytes
	while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
		;

#if 1
	if(rx_buff[0]== 'n')
	{
		for(i=0;i<num_bytes;i++)
		{
			rx_buff[0] = 0;
			while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
				;
			g_buffer[i] = rx_buff[0];

		}
		rx_size = num_bytes;
	}
#endif


	UART_send(&g_uart, (const uint8_t * )"a",1);

	while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
		;
	factor = 1;
	crc = 0;
	while((num_bytes-1)/factor)
	{
		crc = crc^g_buffer[factor];
		factor = factor*2;
	}
	if(crc == (char)rx_buff[0])
	{
		g_src_image_target_address += rx_size;

		UART_send(&g_uart, (const uint8_t * )"a",1);
	}
	else
	{
		UART_send(&g_uart,(const uint8_t * )"n",1);
		goto CRCFAIL;
	}

	return rx_size;
}
uint32_t number_size(uint8_t *ptr)
{
	uint32_t temp = 0,i=0;
	while(*ptr != '\0' && i<9)
	{
		temp = temp*10+*ptr -'0';
		ptr++;
		i++;
	}
	return temp;
}
void copy_to_flash(uint8_t * g_buffer,uint32_t length)
{
	uint32_t i=0;
	for(i=0;i<8;i++)
	{
		FLASH_program(g_flash_address+i*512 , &g_buffer[i*512], 512);
	}
	g_flash_address = g_flash_address + BUFFER_SIZE;
}

void load_spi_flash_with_images_thruough_uart_intf()
{
	volatile uint32_t erase_address = 0;
	volatile uint32_t erase_count=0;
	volatile uint32_t i = 0,length = 0;


	uint8_t rx_buff[8],num[9];
	uint8_t  manufacturer_id;
	uint8_t  device_id,led_state = 0;


	while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
		;
	if(rx_buff[0] == 's') //signal from host PC to proceed to erase the flash.
		UART_send(&g_uart, (const uint8_t * )"a",1);

	FLASH_init();

	FLASH_global_unprotect();
	FLASH_read_device_id
	(
			&manufacturer_id,
			&device_id
	);
	//UART_init( &g_uart, COREUARTAPB0_BASE_ADDR, 59, (DATA_8_BITS | NO_PARITY) );


	erase_address = erase_count = 0;
	for(erase_count = 0;erase_count<=16;erase_count++)  //1MB erase 64
	{
		GPIO_set_output( &g_gpio, GPIO_0,led_state);
		FLASH_erase_64k_block(erase_address);
		delay1(500);
		FLASH_read(erase_address,g_read_buf,32);
		erase_address+=0x10000;
		if(g_read_buf[0] != 0xFF)
		{

			break;
		}
		led_state = led_state ^ 1;
	}
	if(erase_count == 5)
	{
		GPIO_set_output( &g_gpio, GPIO_0,1); //erase successful
	}
	else
	{
		GPIO_set_output( &g_gpio, GPIO_0,0); //erase failed
	}


#if 1
	while(no_of_files<MAX_FILES)
	{

		g_flash_address = flash_address[no_of_files];
		g_src_image_target_address = 0;
		/* start the handshake with the host */
		while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
			;
		if(rx_buff[0] == 'h')
			UART_send(&g_uart, (const uint8_t * )"a",1);
		while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
			;
		if(rx_buff[0] == 'n')
			UART_send(&g_uart, (const uint8_t * )"d", 1 );
		while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
			;
		if(rx_buff[0] == 's')
			UART_send(&g_uart, (const uint8_t * )"h", 1 );
		while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
			;
		if(rx_buff[0] == 'a')
			UART_send(&g_uart, (const uint8_t * )"k", 1 );
		while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
			;
		if(rx_buff[0] == 'e')
		{
			UART_send(&g_uart, (const uint8_t * )"r", 1 );
		}
		/* poll for starting Ack message from the host as an acknowledgment
		                   that the host is ready to send file size */

		while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
			;
		UART_send(&g_uart, (const uint8_t * )"a",1);


		/*poll for file size*/
		UART_send(&g_uart, (const uint8_t * )"z",1);
		i=0;
		while(i<9)
		{
			while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
				;

			num[i] = rx_buff[0];
			i++;
		}
		g_file_size = number_size(num);//;atoi((const char*)rx_buff);

		UART_send(&g_uart, (const uint8_t * )"a",1);

		do
		{
			length = read_page_from_host_through_uart(g_write_buffer, BUFFER_SIZE);
			if(length>0)
			{
				copy_to_flash(g_write_buffer,length);
				//memcpy(ddr_add1,g_page_buffer,length);
				//ddr_add1 +=  length;
			}
			led_state = led_state ^ 1;
			GPIO_set_output( &g_gpio, GPIO_1,led_state);
		}while(length!=0);
		//ddr_add += 0x30000;

		no_of_files++;
	}
	if(no_of_files==MAX_FILES)
	{
		GPIO_set_output( &g_gpio, GPIO_1,1);
	}
#endif
}
#endif
#if 1
void read_program_from_flash_and_copy_to_ddr()
{
	volatile uint32_t i=0,address=flash_address[0],j;
	uint8_t manufacturer_id,device_id;
	UART_polled_tx_string( &g_uart, ( uint8_t*)"\n\rApplication copying to DDR from SPI flash is in progress...\n\r");
	FLASH_init();
	FLASH_global_unprotect();
	FLASH_read_device_id
	(
			&manufacturer_id,
			&device_id
	);
	//ddr memory initialization with 0x55
	for(i=0;i<DDR_APP_MAX_SIZE;i=i+4)
	{
		*(volatile uint32_t*)(DDR_BASE_ADDRESS+i) = 0x55555555;
	}
	for(i=0;i<DDR_APP_MAX_SIZE;i=i+FLASH_SEGMENT_SIZE)
	{
		FLASH_read(address+i , &g_read_buf[0], FLASH_SEGMENT_SIZE);
		//memcpy((uint8_t*)(DDR_BASE_ADDRESS+i),&g_read_buf[0],FLASH_SEGMENT_SIZE);
		for(j=0;j<FLASH_SEGMENT_SIZE;j++)
		{
			*(volatile uint8_t*)(DDR_BASE_ADDRESS+i+j) = g_read_buf[j];
		}
	}
	if(manufacturer_id == 0x20)
	{
		UART_polled_tx_string( &g_uart, ( uint8_t*)"\n\rApplication Successfully copied to DDR from SPI flash\n\r");
	}


}
#endif

int main()
{
	uint8_t rx_data[MAX_RX_DATA_SIZE];
	size_t rx_size;
	char wait_in_bl;

	GPIO_init( &g_gpio, COREGPIO_OUT_BASE_ADDR, GPIO_APB_32_BITS_BUS );

 // flash_check();
 // while(1);


    /**************************************************************************
      * Initialize CoreUARTapb with its base address, baud value, and line
      * configuration.
      *************************************************************************/
	UART_init( &g_uart, COREUARTAPB0_BASE_ADDR,\
			BAUD_VALUE_115200, (DATA_8_BITS | NO_PARITY) );



    /**************************************************************************
     * Display greeting message message.
     *************************************************************************/
	UART_polled_tx_string( &g_uart, g_greeting_msg);

	/*uint8_t  manufacturer_id;
	uint8_t  device_id = 0;
	uint8_t  length = 16;
	uint8_t  temp_write_buffer[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
	uint8_t  temp_read_buf[16] = {0};
	FLASH_init();

	FLASH_global_unprotect();
	FLASH_read_device_id
	(
			&manufacturer_id,
			&device_id
	);

	//FLASH_erase_64k_block(0);

	copy_to_flash(temp_write_buffer,length);

	FLASH_read(0,temp_read_buf,16);*/


#if 0

	uint32_t temp[16] = {0};
	uint32_t *p=0x80200000;
	uint32_t i = 5;

	for (i=0;i<16*1024;i++)
    {
		*p = i;
		p++;
    }

    p=0x80200000;

   // temp =  *(volatile uint32_t*)(DDR_BASE_ADDRESS);
    for (i=0;i<16*1024;i++)
    {
    	//temp[i] = *p;
    	if((*p) != i)
    	{
    		break;
    	}
    	p++;
    }
    temp[5] = 10;
    temp[7] = 100;

#endif

	while(1)
    {

         /**********************************************************************
         * Read data received by the UART.
         *********************************************************************/
    	UART_polled_tx_string( &g_uart, g_instructions_msg);
    	while(!(UART_get_rx ( &g_uart, rx_data, 1 )))
    			;
    	  /**********************************************************************
         * Echo back data received, if any.
         *********************************************************************/


        {
            //UART_send( &g_uart, rx_data, 1 );
    		delay1(1000);
            UART_polled_tx_string( &g_uart, (uint8_t*)"\n\r");
            switch(rx_data[0])
            {
                case '0':
                	UART_polled_tx_string( &g_uart, (uint8_t*)"******** Initiate SPI Programming ******\n\r");
                	load_spi_flash_with_images_thruough_uart_intf();
                    break;
                case '1': // transfer file to DDR
                	read_program_from_flash_and_copy_to_ddr();
                    break;
                case '2':
                	UART_polled_tx_string( &g_uart, (uint8_t*)"Application Execution control will be transferred to DDR\n\r");
                	Bootloader_JumpToApplication((uint32_t)0x80200000, (uint32_t)0x80200004);
                    break;
                case '3':
                	read_program_from_flash_and_copy_to_ddr();
                	UART_polled_tx_string( &g_uart, (uint8_t*)"Application Execution control will be transferred to DDR\n\r");
					Bootloader_JumpToApplication((uint32_t)0x80200000, (uint32_t)0x80200004);
					break;
                default:
                	UART_polled_tx_string( &g_uart, g_instructions_msg);
                	 break;


            }
        }

    }


    while(1);
    return 0;
}

/*
 *  Write to flash on RTG4 Evaluation Kit
 */


/**
 *  Read from flash
 */



#if 0
/*------------------------------------------------------------------------------
 * Count the number of elapsed milliseconds (SysTick_Handler is called every
 * 10mS so the resolution will be 10ms). Rolls over every 49 days or so...
 *
 * Should be safe to read g_10ms_count from elsewhere.
 */
void SysTick_Handler( void )
{
//    uint32_t gpio_pattern;
//    static uint8_t count;
//    /*
//     * Toggle GPIO output pattern by doing an exclusive OR of all
//     * pattern bits with ones.
//     */
//    if(count++>=50)
//    {
//        gpio_pattern = GPIO_get_outputs( &g_gpio );
//        gpio_pattern ^= 0x00000002;
//        GPIO_set_outputs( &g_gpio, gpio_pattern );
//        count=0;
//    }

    g_10ms_count += 10;

     /*
      * For neatness, if we roll over, reset cleanly back to 0 so the count
      * always goes up in proper 10s.
      */
    if(g_10ms_count < 10)
        g_10ms_count = 0;
}
#endif
/*------------------------------------------------------------------------------
 /*------------------------------------------------------------------------------
* Call this function if you want to switch to another program
* de-init any loaded drivers before calling this function
*/

static void Bootloader_JumpToApplication(uint32_t stack_location, uint32_t reset_vector)
{
               /* Restore PLIC to known state: */
               //__disable_irq(); org
				MRV_disable_interrupts();
               // PLIC_init(); org
               MRV_disable_interrupts();

               /* Disable all interrupts: */
               write_csr(mie, 0);

               /* Start executing from the top of DDR memory: */
               __asm volatile("lui ra,0x80200");

    /*
     * Flush the cache.
     */
               __asm volatile ("fence.i");

    /*
     * We need to explicitly execute a return instruction in case the compiler had
     * done some return address register manipulation in this function's veneer.
     */

               __asm volatile("ret");

               /*User application execution should now start and never return here.... */
}



