 /* (c) Copyright 2016-2017 Microsemi SoC Products Group. All rights reserved.
 */
#include "miv_rv32_hal.h"
#include "core_sysservices_pf.h"
#include "core_spi.h"
#include "core_gpio.h"
#include "micron1gflash.h"
#include "core_uart_apb.h"
#include "coreuartapb_regs.h"

#include "hw_platform.h"
#include "helper.h"

#define BUFFER_SIZE 4096

#define IMAGE_IDX                               2u
#define GOLDEN_IMAGE_SPI_ADDRESS 0x400
#define UPDATE_IMAGE_SPI_ADDRESS 0xA00000
#define IAP_IMAGE_SPI_ADDRESS 0x1400000
//extern void delay(volatile int i);
//*&************************************************************
uint8_t g_write_buffer[BUFFER_SIZE];
uint8_t no_of_files =0;
uint32_t flash_address[3] = {GOLDEN_IMAGE_SPI_ADDRESS,UPDATE_IMAGE_SPI_ADDRESS,IAP_IMAGE_SPI_ADDRESS};
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;
gpio_instance_t g_gpio_out;
const uint8_t g_greeting_msg[] =
        " **** PolarFire Programming Example ****\n\r\
Select option:\r\n\
**AUTHENTICATION**\r\n\
1. Bit-stream authentication\r\n\
2. IAP image authentication\r\n\
**DEVICE PROGRAMMING**\r\n\
3. Auto programming\r\n\
4. IAP Program by Index\r\n\
5. IAP Program by address\r\n\
6. Initiate Auto-Update\r\n";


static void display_user_options(void);

uint8_t data_buffer [1024];

static void clear_data_buffer(void)
{
    uint32_t idx=0;

    while( idx < 1024 )
    {
        data_buffer[idx++] = 0x00;
    }
}
/***************************************************************************//**
 * main function.
 */
UART_instance_t g_uart;

#define MAX_FILES 3
void delay1(volatile uint32_t n)
{
    while(n)
        n--;
}
void write_flash_directory()
{
    uint32_t address1 = 0x400,address2 = 0xA00000,address3 = 0x1400000;
    uint8_t buf[4];
    buf[3] = (uint8_t)((address1 >> 24) & 0xFF);
    buf[2] = (uint8_t)((address1 >> 16) & 0xFF);
    buf[1] = (uint8_t)((address1 >> 8) & 0xFF);
    buf[0] = (uint8_t)(address1 & 0xFF);
    FLASH_program(0 , buf, 4);
    FLASH_read(0,g_read_buf,4);
    buf[3] = (uint8_t)((address2 >> 24) & 0xFF);
    buf[2] = (uint8_t)((address2 >> 16) & 0xFF);
    buf[1] = (uint8_t)((address2 >> 8) & 0xFF);
    buf[0] = (uint8_t)(address2 & 0xFF);
    FLASH_program(4 , buf, 4);
    FLASH_read(4,g_read_buf,4);
    buf[3] = (uint8_t)((address3 >> 24) & 0xFF);
    buf[2] = (uint8_t)((address3 >> 16) & 0xFF);
    buf[1] = (uint8_t)((address3 >> 8) & 0xFF);
    buf[0] = (uint8_t)(address3 & 0xFF);
    FLASH_program(8 , buf, 4);
    FLASH_read(8,g_read_buf,4);
}
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;
}

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;

    UART_init( &g_uart, COREUARTAPB0_BASE_ADDR, BAUD_VALUE_256000, (DATA_8_BITS | NO_PARITY) );
    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 )))
        ;
    //for(i=0;i<2500;i++);
    //transmit the address to the host
#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++);
        //UART_send(&g_uart, (const uint8_t * )&temp, 4 );
        //UART_polled_tx_string(&g_uart, (const uint8_t * )&temp);
    }

    //poll for Ack message from the host as an acknowledgment that the host received the address
    while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
        ;
#endif
    //transmit the returnbytes to the host
    temp_add[0] = num_bytes&0xFF;
    temp_add[1] = (num_bytes>>8)&0xFF;
    if(rx_buff[0]== 'a')
    {
        //UART_send(&g_uart, (const uint8_t * )&num_bytes, 4 );
        //UART_polled_tx_string(&g_uart,(const uint8_t * )&num_bytes);
        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 returnbytes
    while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
        ;
    /*if(rx_buff[0]== 'f')
    {
        UART_send(&g_uart, (const uint8_t * )&num_bytes, 4 ); // sending the num of bytes again... as previous failed
        while(!(UART_get_rx ( &g_uart, rx_buff, 1 )))
                ;
    }*/
    //for(i=0;i<2000;i++);
    //read the data from the host for the request number of bytes
#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

    //send Ack message to indicate one transaction is done
    UART_send(&g_uart, (const uint8_t * )"a",1);
    //Recive 1-byte CRC for data of size num_bytes
    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 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<=640;erase_count++)  //40MB erase 640
    {
        GPIO_set_output( &g_gpio_out, 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 == 641)
    {
        GPIO_set_output( &g_gpio_out, GPIO_0,1); //erase successful
    }
    else
    {
        GPIO_set_output( &g_gpio_out, GPIO_0,0); //erase failed
    }
    write_flash_directory();

#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_out, GPIO_1,led_state);
        }while(length!=0);
        //ddr_add += 0x30000;
        UART_init( &g_uart, COREUARTAPB0_BASE_ADDR, BAUD_VALUE_256000, (DATA_8_BITS | NO_PARITY) );
        no_of_files++;
    }
    if(no_of_files==MAX_FILES)
    {
        GPIO_set_output( &g_gpio_out, GPIO_1,1);
    }
#endif
}

#if 0 //will be supported  Rev E silicon onwards
void execute_digest_check(void)
{
    uint32_t options = 0x4;
    uint32_t mb_offset = 0;
    uint8_t uart_input[8]={0};
    uint16_t ret_val = 0;
    uint32_t* ptr = 0;
    uint32_t count=0;

    UART_polled_tx_string(&g_uart,(const uint8_t*)"\r\nDigest check for sNVM is in progress...");

    ret_val = SYS_digest_check_service(options, mb_offset);

    if(0 == ret_val)
    {
        UART_polled_tx_string(&g_uart,(const uint8_t*)"\r\nDigest check Success.");
    }
    else
    {
        UART_polled_tx_string(&g_uart,(const uint8_t*)"\r\nDigest check return value: ");
        display_output((uint8_t*)&ret_val, 1);
    }
    UART_polled_tx_string(&g_uart,(const uint8_t*)"\r\n\r\n");
}
#endif


void execute_usercode_service(void)
{
    uint8_t status,code[4];

    UART_polled_tx_string(&g_uart, (const uint8_t*)"32bit USERCODE/Silicon Signature (MSB first): ");
    status = SYS_get_user_code(data_buffer, 0);
    code[0] = data_buffer[3];
    code[1] = data_buffer[2];
    code[2] = data_buffer[1];
    code[3] = data_buffer[0];
    if(SYS_SUCCESS == status)
    {
        display_output(code, USERCODE_RESP_LEN);
    }
    else
    {
        UART_polled_tx_string(&g_uart,(const uint8_t*)"USERCODE Service failed.\n\r");

    }

    UART_polled_tx_string(&g_uart, (const uint8_t*)"\n\r");
}

void execute_designinfo_service(void)
{
    uint8_t status;
    status = SYS_get_design_info(data_buffer, 0);
    if(SYS_SUCCESS == status)
    {

        UART_polled_tx_string(&g_uart, (const uint8_t*)"\r\nDesign Version(MSB first): ");
        display_output((data_buffer + 33), 1);
        display_output((data_buffer + 32), 1);

        UART_polled_tx_string(&g_uart,(const uint8_t*)"\r\n");
    }
    else
    {
        UART_polled_tx_string(&g_uart,(const uint8_t*)"DesignInfo Service failed.\r\n");
    }


}

static void display_user_options(void);

uint8_t data_buffer [1024];

/*==============================================================================
  Display greeting message when application is started.
 */
static void display_user_options(void)
{
    UART_polled_tx_string(&g_uart, g_greeting_msg);
}




void execute_iap(uint8_t option)
{
    uint8_t cmd = 0;
    uint32_t spiaddr_or_idx = 0;
    uint8_t status = 0xFF;
    switch(option)
    {
    case '4':
        UART_polled_tx_string(&g_uart, (const uint8_t*)"\r\nIAP PROGRAM for image at index 2 is in progress...\n\rIt takes approximately 28 seconds\n\r");

        cmd = IAP_PROGRAM_BY_SPIIDX_CMD;
        spiaddr_or_idx = IMAGE_IDX;
        break;
    case '5':
        UART_polled_tx_string(&g_uart, (const uint8_t*)"\r\nIAP PROGRAM for image at address 0x1400000 is in progress...\n\rIt takes approximately 28 seconds\n\r");
        cmd = IAP_PROGRAM_BY_SPIADDR_CMD;
        spiaddr_or_idx = IAP_IMAGE_SPI_ADDRESS;
        break;
    case '6':
        UART_polled_tx_string(&g_uart, (const uint8_t*)"\r\nAuto update is in progress...\r\nIt takes approximately 28 seconds\n\r");

        cmd = IAP_AUTOUPDATE_CMD;
        break;

    default: UART_polled_tx_string(&g_uart,(const uint8_t*)
            "Invalid input.\r\n");
    }


    status = SYS_iap_service(cmd, spiaddr_or_idx);
    UART_polled_tx_string(&g_uart, (const uint8_t*)"\r\nStatus code returned: ");
    display_output(&status, 1);
    UART_polled_tx_string(&g_uart, (const uint8_t*)"\r\n");
}

void execute_bitstream_authenticate(void)
{

    uint32_t mb_offset = 0;
    uint8_t status = 0xFF;
    UART_polled_tx_string(&g_uart, (const uint8_t*)"\r\nBitstream authentication for image at address 0x1400000 is in progress...\n\r");
    status = SYS_bitstream_authenticate_service(IAP_IMAGE_SPI_ADDRESS, mb_offset);
    UART_polled_tx_string(&g_uart, (const uint8_t*)"Authentication status: ");
    if(SYS_SUCCESS == status)
    {
        UART_polled_tx_string(&g_uart, (const uint8_t*)"SUCCESS ");
    }
    else
    {
        display_output(&status, 1);
    }
    UART_polled_tx_string(&g_uart, (const uint8_t*)"\r\n");
}

void execute_iap_image_authenticate(void)
{

    uint8_t status = 0xFF;


    UART_polled_tx_string(&g_uart, (const uint8_t*)"\r\nIAP image authentication for image at index 2 is in progress...\n\r");
    status = SYS_IAP_image_authenticate_service(IMAGE_IDX);
    UART_polled_tx_string(&g_uart, (const uint8_t*)"Authentication status: ");
    if(SYS_SUCCESS == status)
    {
        UART_polled_tx_string(&g_uart, (const uint8_t*)"SUCCESS ");
    }
    else
    {
        display_output(&status, 1);
    }
    UART_polled_tx_string(&g_uart, (const uint8_t*)"\r\n");

}
uint8_t  GPIO_Init()
{
    uint32_t gpio_inputs =0;
    uint8_t ret = 0;
    GPIO_init(&g_gpio_out, COREGPIO_BASE_ADDR, GPIO_APB_32_BITS_BUS);

    /**************************************************************************
     * Set the GPIO outputs.
     *************************************************************************/
    GPIO_set_output( &g_gpio_out, GPIO_0,1);
    GPIO_set_output( &g_gpio_out, GPIO_1,1);
    gpio_inputs = GPIO_get_inputs(&g_gpio_out);
    if(gpio_inputs & 0x4)
    {
        ret = 1;
    }
    else
    {
        ret = 0;
    }
    return ret;
}
void programming_options()
{
    size_t rx_size;
    uint8_t rx_buff[1] = {0x00},mode = 0;
    SYS_init(CSS_PF_BASE_ADDRESS);
    execute_designinfo_service();
    execute_usercode_service();
    clear_data_buffer();


    display_user_options();

    for(;;)
    {
        /* Start command line interface if any key is pressed. */
        rx_size = UART_get_rx(&g_uart, rx_buff, sizeof(rx_buff));
        if(rx_size > 0)
        {
            UART_polled_tx_string(&g_uart, (const uint8_t*)"\n\r");
            switch(rx_buff[0])
            {
            case '1':
                execute_bitstream_authenticate();
                break;
            case '2':
                execute_iap_image_authenticate();
                break;
            case '3':
                UART_polled_tx_string(&g_uart,(const uint8_t*)"Erase the PolarFire device using the FlashPro ERASE action.\n\rNext Power cycle the board to initiate AutoProgramming on Blank device.\r\n");

                break;
            case '4':
            case '5':
            case '6':
                execute_iap(rx_buff[0]);
                break;
            case '7':
                //execute_digest_check();
                break;
            default:
                display_user_options();
                break;
            }
            display_user_options();
        }
    }
}
int main()
{


    uint8_t mode = 2;

    mode = GPIO_Init();




    switch(mode)
    {
    case 0:
        UART_init( &g_uart, COREUARTAPB0_BASE_ADDR, BAUD_VALUE_256000, (DATA_8_BITS | NO_PARITY) );
        load_spi_flash_with_images_thruough_uart_intf();
        break;
    case 1:
        UART_init( &g_uart, COREUARTAPB0_BASE_ADDR, BAUD_VALUE_115200, (DATA_8_BITS | NO_PARITY) );
        programming_options();
        break;

    }
    while(1)
    {

    }

    return 0;
}




