/*-----------------------------------------------------------------------------
//     $Date: 2004/10/21 01:24:01 $
//     $RCSfile: flash_load.c,v $
//-----------------------------------------------------------------------------
//
//     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
//     SOLELY FOR USE IN DEVELOPING PROGRAMS AND SOLUTIONS FOR
//     XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE, OR INFORMATION
//     AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION
//     OR STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS
//     IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
//     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
//     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
//     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
//     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
//     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
//     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//     FOR A PARTICULAR PURPOSE.
//     
//     (c) Copyright 2004 Xilinx, Inc.
//     All rights reserved.
//
//---------------------------------------------------------------------------*/

#include <stdio.h>
#include <sysace_stdio.h>
#include "xbasic_types.h"
#include "xstatus.h"
#include "xio.h"
#include "xuartns550_l.h"
#include "xflash_intel_l.h"
#include "xparameters.h"
#include "xromlcd.h"

#define DEBUG 0
#define MAXERRORS 100
#define FLASH_BASEADDR  0x28000000UL
#define FLASH_HIGHADDR  0x287FFFFFUL
#define FLASH_CONTROL_FILE "A:\\XILINX\\flash.txt"
#define ZBT_BASEADDR    0x20000000UL

#define NUM_PARTS       2
#define BLOCKSIZE       (NUM_PARTS*128*1024)    /* part has a 128KB block */

#define BUFSIZE         (1*BLOCKSIZE)           /* multiple of BLOCKSIZE */
#define MAXFILENAMESIZE 128
#define MAXFILES        32

#if DEBUG
#define debug_print(x)   xil_printf(x)
#define debug_putnum(x)  putnum(x)
#define debug_printch(x) XUartNs550_SendByte(UART_BASEADDR, (x))
#else
#define debug_print(x)
#define debug_putnum(x)
#define debug_printch(x)
#endif

#define UART_BASEADDR XPAR_OPB_UART16550_0_BASEADDR
#define UART_CLOCK    XPAR_XUARTNS550_CLOCK_HZ

#if !SIM
#define UART_BAUDRATE 9600                      /* real hardware */
#else
#define UART_BAUDRATE (UART_CLOCK / 16 / 3)     /* simulation */
#endif

/*---------------------------------------------------------------------------*/

enum DOWNLOAD_SOURCES { SYSTEMACE, MEMORY };
typedef enum DOWNLOAD_SOURCES DOWNLOAD_TYPE;
typedef enum {BIT, DATA} DOWNLOAD_FILE_TYPE;

/*---------------------------------------------------------------------------*/

/* Global variables */
Xuint32 GAddresses[MAXFILES];
char GFileNames[MAXFILES][MAXFILENAMESIZE];
char* ShortGFileNames = "...              ";
DOWNLOAD_FILE_TYPE GFileTypes[MAXFILES];
Xuint16 GNumFiles = 0;

/*---------------------------------------------------------------------------*/

static Xboolean Flash_Erase (Xuint32 BaseAddress, Xuint32 StartAddress,
                             Xuint32 EndAddress)
{
  XStatus status;
  Xuint32 offset;
  static Xuint32 bitmask = ~(BLOCKSIZE - 1);

  /* Truncate StartAddress and EndAddress to nearest block boundary.
     Only entire blocks can be erased. */
  StartAddress = StartAddress & bitmask;
  EndAddress = EndAddress & bitmask;

#if DEBUG
  xil_printf("Erasing entire blocks from 0x%0X to 0x%0X (inclusive)\r\n",
             StartAddress, EndAddress);
#endif

  /* Erase all blocks between StartAddress and EndAddress inclusive */
  for (offset=StartAddress-BaseAddress; offset <= EndAddress-BaseAddress;
       offset += BLOCKSIZE)
    {
      status = XFlashIntel_EraseAddr(BaseAddress, offset);
      if (status != XST_SUCCESS)
        {
          xil_printf("Failed to erase block with starting address 0x%0X\r\n",
                     BaseAddress + offset);
          return XTRUE;
        }
    }
  return XFALSE;
}

#define Flash_Write(BaseAddress, StartAddress, Buffer, Size) \
  XFlashIntel_WriteAddr(BaseAddress, (StartAddress)-(BaseAddress), Buffer, Size)

static Xboolean Flash_Verify (Xuint32 BaseAddress, Xuint32 StartAddress,
                              Xuint8 *Buffer, unsigned int Size)
{
  unsigned int i;
  Xuint8 val;

  for (i=0; i < Size; i++, StartAddress++, Buffer++)
    {
      val = XIo_In8(StartAddress);
      if (val != *Buffer)
        {
          xil_printf("ERROR - at 0x%0X, got 0x%0X, expected 0x%0X\n\r",
                     StartAddress, val, *Buffer);
          return XTRUE;
        }
    }
  return XFALSE;
}

static Xboolean Memory_Load_File
(
 XIo_Address StartAddress,
 SYSACE_FILE *fileptr,
 char *SysaceBuffer,
 const Xuint32 BufferSize
 )
{
  Xuint32 i;
  Xuint32 numread;

  xil_printf("Loading to Memory....");

  do
    {
      numread = sysace_fread(SysaceBuffer, 1, BufferSize, fileptr);
      for (i=0; i < numread; i++)
        {
          XIo_Out8(StartAddress++, *SysaceBuffer++);
        }
    } while (numread);

  if (fileptr)
    sysace_fclose(fileptr);

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

  return XFALSE;
}

static Xboolean Flash_Load_File
(
 Xuint32 BaseAddress, Xuint32 StartAddress,
 SYSACE_FILE *fileptr, Xuint8 *SysaceBuffer,  /* SystemACE */
 Xuint32 SrcAddress, Xuint32 SrcSize,  /* Memory */
 DOWNLOAD_TYPE Type, Xuint8 MaxRetries
 )
{
  Xuint8 *bufptr;
  Xuint32 numread;
  Xuint8 retries;
  Xboolean failure;

  xil_printf("Loading to Flash....");

  if (StartAddress & ((Xuint32) (BLOCKSIZE - 1)))
    {
      xil_printf("Attempting to store bitstream starting at 0x%0X\n\r",
                 StartAddress);
      xil_printf("The address must be a multiple of 0x%0X\n\r", BLOCKSIZE);
      return XTRUE;
    }

  do {
    retries = 0;
    switch (Type)
      {
      case SYSTEMACE:
        numread = sysace_fread(SysaceBuffer, 1, BUFSIZE, fileptr);
        bufptr = SysaceBuffer;
        break;
      case MEMORY:
        xil_printf("System Memory -> Flash copying is not supported yet\n\r");
        return XTRUE;
        break;
      }

    if (numread)
      {
        failure = 0;
        retries = 0;
        do {
          if (retries > 0)
            xil_printf("Retrying write to 0x%0X block\n\r", StartAddress);
          if (Flash_Erase(BaseAddress, StartAddress, StartAddress))
            {
              xil_printf("Failed to erase block at 0x%0X\n\r", StartAddress);
              failure = 1;
              retries++;
              continue;
            }
#if DEBUG
          xil_printf("Writing 0x%0X bytes to 0x%0X\n\r",
                     numread, StartAddress);
#endif
          if (Flash_Write(BaseAddress, StartAddress, bufptr, numread) != numread)
            {
              xil_printf("Failed to write block at 0x%0X\n\r", StartAddress);
              failure = 1;
              retries++;
              continue;
            }
#if DEBUG
          xil_printf("Verifying 0x%0X to 0x%0X\n\r",
                     StartAddress, StartAddress + numread);
#endif
          if (Flash_Verify(BaseAddress, StartAddress, bufptr, numread))
            {
              xil_printf("Verification failed at block 0x%0X\n\r",
                         StartAddress);
              failure = 1;
              retries++;
            }
        } while (failure && (retries <= MaxRetries));
      }

    if (failure)
      {
        if (fileptr)
          sysace_fclose(fileptr);
        return XTRUE;
      }

    StartAddress += numread;
  } while (numread);

  if (fileptr)
    sysace_fclose(fileptr);

  xil_printf("successful.\n\r");
  return XFALSE;
}

/*---------------------------------------------------------------------------*/

static Xboolean Print_File (char *FileName)
{
  SYSACE_FILE *fileptr;
  Xuint32 numread;
  char Buffer[1];

  fileptr = sysace_fopen(FileName, "r");

  if (!fileptr)
    {
      xil_printf("ERROR - Can't open file %s on the Compact Flash\n\r",
                 FileName);
      return XTRUE;
    }

  do
    {
      numread = sysace_fread(Buffer, 1, 1, fileptr);
      if (numread)
        {
          xil_printf("%c", *Buffer);
        }
    } while (numread);

  xil_printf("\n\r");

  sysace_fclose(fileptr);

  return XFALSE;
}

static Xboolean Flash_Sysace_Control_File_Read (char *ControlFileName)
{
  SYSACE_FILE *fileptr;
  Xuint32 numread;
  char Buffer[1];
  int i;
  Xuint16 numfiles = 0;

  xil_printf("Reading Configuration File '%s' to determine files to read\n\r", ControlFileName);
  xil_printf("  from Compact Flash and load into memory\n\r");

  fileptr = sysace_fopen(ControlFileName, "r");
  if (!fileptr)
    {
      xil_printf("ERROR - Can't open flash configuration file %s on the Compact Flash\n\r",
                 ControlFileName);
      return XTRUE;
    }

  do
    {
      numread = sysace_fread(Buffer, 1, 1, fileptr);
      switch (*Buffer)
        {
        case ' ':
        case '\t':
        case '\r':
        case '\n':
          break;
        case '0':
          numread = sysace_fread(Buffer, 1, 1, fileptr);
          if (numread && (*Buffer == 'x'))
            {
              /* Hex address */
              GAddresses[numfiles] = 0;
              for (i=0; i<8; i++)
                {
                  GAddresses[numfiles] <<= 4;
                  numread = sysace_fread(Buffer, 1, 1, fileptr);
                  if (!numread)
                    {
                      xil_printf("ERROR - end of file reached with incomplete address\n\r");
                      return XTRUE;
                    }
                  if (isalpha(*Buffer))
                    GAddresses[numfiles] |= toupper(*Buffer) - 'A' + 10;
                  else
                    GAddresses[numfiles] |= *Buffer - '0';
                }
              /* Skip whitespace */
              do {
                numread = sysace_fread(Buffer, 1, 1, fileptr);
                if (!numread)
                  {
                    xil_printf("ERROR - end of file reached without file type\n\r");
                    return XTRUE;
                  }
              } while (numread && ((*Buffer == ' ') || (*Buffer == '\t')));
              /* File type is a single character to make life easier */
              switch (toupper(*Buffer))
                {
                case 'B':
                  GFileTypes[numfiles] = BIT;
                  break;
                case 'D':
                  GFileTypes[numfiles] = DATA;
                  break;
                default:
                  xil_printf("ERROR - unknown file type %c\n\r", Buffer);
                  return XTRUE;
                }
              /* Skip whitespace */
              do {
                numread = sysace_fread(Buffer, 1, 1, fileptr);
                if (!numread)
                  {
                    xil_printf("ERROR - end of file reached without file name\n\r");
                    return XTRUE;
                  }
              } while (numread && ((*Buffer == ' ') || (*Buffer == '\t')));
              /* Filename is everything that's not whitespace */
              i = 0;
              while (numread && !(isspace(*Buffer)))
                {
                  GFileNames[numfiles][i++] = *Buffer;
                  numread = sysace_fread(Buffer, 1, 1, fileptr);
                }
              GFileNames[numfiles][i] = '\0';
              numfiles++;
            }
          break;
        case '#':
          /* Comment to the end of the line */
          do
            {
              numread = sysace_fread(Buffer, 1, 1, fileptr);
            } while (numread && (*Buffer != '\n'));
          break;
        default:
          break;
        }
    } while (numread);

  sysace_fclose(fileptr);

  GNumFiles = numfiles;

  for (i=0; i < numfiles; i++)
    {
      xil_printf("Address 0x%0X = %s\n\r", GAddresses[i], GFileNames[i]);
    }
  return XFALSE;
}

/*---------------------------------------------------------------------------*/

static SYSACE_FILE *Flash_Process_Bit_File
(
 char *FileName
)
{
  SYSACE_FILE *fileptr;
  int skip_index;
  char Buffer[1];
  int numread;
  const Xuint8 match_array[] = 
    { 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x99, 0x55, 0x66 };
  const int match_length = 8;
  int match_index;
  int i;

#if DEBUG
  xil_printf("Load .bit file %s\n\r", FileName);
#endif

  fileptr = sysace_fopen(FileName, "r");
  if (!fileptr)
    {
      xil_printf("ERROR - Can't open %s on compact Flash\n\r",
                 FileName);
      return XNULL;
    }

  /* Look for 4 consecutive FF bytes and then AA995566 */
  skip_index = 0;
  match_index = 0;
  while ((numread = sysace_fread(Buffer, 1, 1, fileptr))
         && (match_index != match_length))
    {
      if ((Xuint8) *Buffer == match_array[match_index])
        {
          /* still matching */
          match_index++;
        }
      else
        {
          /* no longer matching, reset */
          skip_index += match_index + 1;
          match_index = 0;
        }
    }
  sysace_fclose(fileptr);

  /* didn't find bitstream */
  if (match_index != match_length)
    return XNULL;

  /* Reopen file and skip header, return pointer to start of bitstream. */
  fileptr = sysace_fopen(FileName, "r");
  if (!fileptr)
    {
      xil_printf("ERROR - Can't open %s on compact Flash\n\r",
                 FileName);
      return XNULL;
    }

  xil_printf("Skipping %d header bytes of .bit file\n\r", skip_index);

  for (i=0; i < skip_index; i++)
    {
      numread = sysace_fread(Buffer, 1, 1, fileptr);
      if (!numread)
        return XNULL;
    }

  return fileptr;
}

/*---------------------------------------------------------------------------*/

int main()
{
  /* static char sysace_buffer[BUFSIZE]; */
  /* We need to hard-code the buffer location so that the entire program
     can exist in block ram.  When genace works better, can use linker
     script again. */
  static char *sysace_buffer = (char *) ZBT_BASEADDR;
  SYSACE_FILE *fileptr;
  int i,j;

  XUartNs550_SetBaud(UART_BASEADDR, UART_CLOCK, UART_BAUDRATE);
  XUartNs550_mSetLineControlReg(UART_BASEADDR, XUN_LCR_8_DATA_BITS);

  /* print directions to LCD screen */
  XromLCDOn();
  XromLCDInit();
  XromLCDPrintString ("ML401 Flash", "Loader");

  xil_printf("\n\r=========================\n\r");
  xil_printf("Xilinx ML401 Flash Loader\n\r");
  xil_printf("=========================\n\r");

#if DEBUG
  xil_printf("\n\rEchoing Flash Configuration File\n\r\n\r");
  Print_File(FLASH_CONTROL_FILE);
  xil_printf("\n\rEnd of Echo\n\r");
#endif

  if (Flash_Sysace_Control_File_Read(FLASH_CONTROL_FILE))
    return 1;

  for (i=0; i < GNumFiles; i++)
    {
      xil_printf("\n\rCopying %s -> 0x%0X\n\r",
                 GFileNames[i], GAddresses[i]);
      ShortGFileNames = "...             ";
      for (j=3; j<17; j++) {
        if (strlen (GFileNames[i]) <= 13)
  	  ShortGFileNames[j] = GFileNames[i][j-3];
        else
          ShortGFileNames[j] = GFileNames[i][strlen (GFileNames[i]) - 16 + j];
        if (ShortGFileNames[j] == '\\')
          ShortGFileNames[j] = '/';
      }
      XromLCDPrintString ("Loading to Flash", ShortGFileNames);
      switch (GFileTypes[i])
        {
        case BIT:
          xil_printf("Opening BIT file %s and stripping header\n\r",
                     GFileNames[i]);
          if (!(fileptr = Flash_Process_Bit_File(GFileNames[i])))
            return 1;
          break;
        case DATA:
          xil_printf("Opening DATA file %s\n\r",
                     GFileNames[i]);
          if (!(fileptr = sysace_fopen(GFileNames[i], "r")))
            {
              xil_printf("ERROR - Can't open %s on compact Flash\n\r",
                         GFileNames[i]);
            }
          break;
        }
      if ((FLASH_BASEADDR <= GAddresses[i])
          && (GAddresses[i] <= FLASH_HIGHADDR))
        {
          if (Flash_Load_File(FLASH_BASEADDR, GAddresses[i],
                              fileptr, sysace_buffer,
                              0, 0, SYSTEMACE, 2))
            {
              xil_printf("ERROR - Unsuccessful copy\n\r");
              return 1;
            }
        }
      else
        {
          xil_printf("Address 0x%08X is not within Flash memory\n\r",
                     GAddresses[i]);
          xil_printf("Assuming normal memory exists at this address\n\r");
          if (Memory_Load_File(GAddresses[i], fileptr, sysace_buffer,
                               BUFSIZE))
            {
              xil_printf("ERROR - Unsuccessful copy\n\r");
              return 1;
            }
        }
    }

  xil_printf("\n\rFLASH LOAD COMPLETED SUCCESSFULLY\n\r");
  xil_printf("=================================\n\r");

  XromLCDPrintString ("FLASH LOAD      ","COMPLETED!      ");

  while (1) {};
  return 0;

}
