//******************************************************************************
//* LOGSYS SD card bootloader.                                                 *
//* Written by   : Tamas Raikovich                                             *
//* Version      : 1.0                                                         *
//* Last modified: 2013.10.13.                                                 *
//******************************************************************************
#include <xparameters.h>
#include <xintc_l.h>
#include <mb_interface.h>
#include <stdio.h>
#include <string.h>
#include "timer\timer.h"
#include "spi\spi.h"
#include "memcard\memory_card.h"
#include "memcard\fat_filesystem.h"


//******************************************************************************
//* Settings related to the boot image file.                                   *
//******************************************************************************
#define BOOTIMAGE_NAME		"/BOOTIMG/LINSP6LE.IMG"
#define START_ADDRESS		XPAR_SDRAM_S_AXI_BASEADDR


//******************************************************************************
//* Type definitions.                                                          *
//******************************************************************************
typedef struct _SectionData {
	unsigned long size;
	unsigned long baseAddr;
	unsigned long fileOffset;
	unsigned long isLast;
} SectionData, *PSectionData;


//******************************************************************************
//* Global variables.                                                          *
//******************************************************************************
FATFS fatfs;
SectionData sections[64];


//******************************************************************************
//* Multi-byte word access functions.                                          *
//******************************************************************************
unsigned long ReadDWORD(void *addr)
{
    unsigned char *ptr = (unsigned char *)addr;
    unsigned long value = 0;

    value |= (unsigned long)*ptr++;
    value |= ((unsigned long)*ptr++) << 8;
    value |= ((unsigned long)*ptr++) << 16;
    value |= ((unsigned long)*ptr) << 24;

    return value;
}


//******************************************************************************
//* Endless loop.                                                              *
//******************************************************************************
void Halt()
{
	while (1);
}


//******************************************************************************
//* Main function.                                                             *
//******************************************************************************
int main()
{
	unsigned long i;
	unsigned long result;
	unsigned long *ptr;
	unsigned short bytesRead;
	unsigned short bytesToRead;
	unsigned long remainingBytes;
	unsigned char *address;
	unsigned long totalBytes;
	unsigned long processedBytes;
	unsigned long percent;
	SectionData *currentSection;
	void (*entryPoint)(void);

	//Enable the interrupts on the MicroBlaze processor.
	microblaze_enable_interrupts();
	//Enable the interrupts on the interrupt controller.
	XIntc_MasterEnable(XPAR_INTC_0_BASEADDR);

	//Initialize the timer.
	TimerInitialize(100);

	//Display the initial message.
	xil_printf("\x1b[2J");
	xil_printf("\x1b[0;0H");
	xil_printf("LOGSYS SD card bootloader\r\n");
	xil_printf("Copyright (C) 2013, Tamas Raikovich\r\n");

	//Initializing the memory card.
	xil_printf("Initializing the memory card......");
	result = MemCardInitialize();
	if (result != DISK_OK)
	{
		xil_printf("Error %d\r\n", result);
		Halt();
	}
	xil_printf("OK\r\n");

	//Mounting the filesystem.
	xil_printf("Mounting the filesystem...........");
	result = pf_mount(&fatfs);
	if (result != FR_OK)
	{
		xil_printf("Error %d\r\n", result);
		Halt();
	}
	xil_printf("OK (");
	switch (fatfs.fs_type)
	{
		case FS_FAT12:
			xil_printf("FAT12)\r\n");
			break;
		case FS_FAT16:
			xil_printf("FAT16)\r\n");
			break;
		case FS_FAT32:
			xil_printf("FAT32)\r\n");
			break;
	}

	//Opening the image file.
	xil_printf("Loading the image file............");
	result = pf_open(BOOTIMAGE_NAME);
	if (result != FR_OK)
	{
		xil_printf("Error %d\r\n", result);
		Halt();
	}

	//Read the section data from the beginning of the image file.
	result = pf_read(sections, sizeof(sections), &bytesRead);
	if (result != FR_OK)
	{
		xil_printf("Error %d\r\n", result);
		Halt();
	}

	//Correct the byte order when the system is big-endian.
	ptr = (unsigned long *)sections;
	for (i = 0; i < (sizeof(sections) / 4); i++)
	{
		*ptr = ReadDWORD(ptr);
		ptr++;
	}

	//Calculate the total size of the sections.
	currentSection = &sections[0];
	totalBytes = 0;
	processedBytes = 0;
	for (i = 0; i < (sizeof(sections) / sizeof(SectionData)); i++)
	{
		//Check the base address of the section.
		if (currentSection->baseAddr < START_ADDRESS)
			continue;

		//Add the section size to the total number of bytes.
		totalBytes += currentSection->size;
		if (currentSection->isLast)
			break;
		currentSection++;
	}

	//Load the content of each section into the memory.
	xil_printf("000 %%");
	currentSection = &sections[0];
	for (i = 0; i < (sizeof(sections) / sizeof(SectionData)); i++)
	{
		//Check the base address of the section.
		if (currentSection->baseAddr < START_ADDRESS)
			continue;

		if (currentSection->fileOffset == 0)
		{
			//Clear the memory area for this section.
			memset((void *)currentSection->baseAddr, 0, currentSection->size);

			//Update the progress value.
			processedBytes += currentSection->size;
			percent = (100 * processedBytes) / totalBytes;
			xil_printf("\x1b[5D");
			xil_printf("%03d %%", percent);
		}
		else
		{
			//Set the file pointer.
			result = pf_lseek(currentSection->fileOffset);
			if (result != FR_OK)
			{
				xil_printf("\x1b[5D");
				xil_printf("Error %d\r\n", result);
				Halt();
			}

			//Read the data into the memory.
			address = (unsigned char *)currentSection->baseAddr;
			remainingBytes = currentSection->size;

			while (remainingBytes > 0)
			{
				bytesToRead = (remainingBytes > 32768) ? 32768 : (unsigned short)remainingBytes;

				result = pf_read(address, bytesToRead, &bytesRead);
				if (result != FR_OK)
				{
					xil_printf("\x1b[5D");
					xil_printf("Error %d\r\n", result);
					Halt();
				}

				remainingBytes -= bytesToRead;
				address += bytesToRead;

				//Update the progress value.
				processedBytes += bytesToRead;
				percent = (100 * processedBytes) / totalBytes;
				xil_printf("\x1b[5D");
				xil_printf("%03d %%", percent);
			}
		}

		if (currentSection->isLast)
			break;
		currentSection++;
	}

	xil_printf("\r\n\r\n");

	//Jump to the start address.
	entryPoint = (void (*)(void))START_ADDRESS;
	entryPoint();

	return 0;
}


