/************************* Project Include Files *****************************/
/*general includes */
#include <hidef.h>  /* also includes boolean definitions in stdtypes.h     */
#include "s12x_peripherals.h"
#include "target.h" /* includes peripherals definitions and FSL data types */
#include "far_ptr.h"
/*application includes */
#include "main.h"
#include "s12xe_flash.h"
/*driver includes */


/************************* #defines ******************************************/

/************************* Constants *****************************************/


/************************* Macros ********************************************/



/************************* Global Variables **********************************/ 
volatile tMSCAN 	*CAN_Ptr @ 0x3fd0;        /* Global pointer used to store address of CAN module to use */

volatile tREG08 Flags @  0x3fd9;
tU08 SCI_or_CAN; /* Global variable. Bit used to determine which interface is used: 0: SCI, 1:CAN */

volatile tSCI 		*SCI_Ptr	@ 0x3fd2;       /* Global pointer used to store address of SCI module to use */

/************************* function prototypes *******************************/
void SCI_Rx(void);
void CAN_Rx(void);
void main(void);	
void Delay(unsigned int delayTime);

/******************************************************************************
Function Name  : main
Engineer       : b06321	
Date           : 01/11/2006
Parameters     : NONE
Returns        : NONE
Notes          : main routine called by reset vector
******************************************************************************/

void main(void) 
{ 
 	                  /* ensure interrupts are disabled */
    asm sei;					
				            /* clear the illegal address reset flag */
		CRG.crgflg.bit.ilaf = 1;		 
                    /* initialise the memory controller prior to any  */
                    /* commands being launched or EEE data access     */
		while(!FTM.fstat.bit.ccif){}        /* wait for the FTM to be ready */
    FTM.fclkdiv.byte = 3;   /* load the clock divider before any flash command. Assumes 4MHz XTAL */              											
		

    while (!FTM.fstat.bit.ccif) {}					/* wait for FTM reset to complete */
   
    Delay(1000);
    while(1)
    {
      SCI_or_CAN = Flags.bit._3;  
      if (!SCI_or_CAN) 
      {                      
			  	          /* erase reset vectors */
			  LaunchFlashCommand(2, ERASE_P_FLASH_SECTOR, 0x7F, 0xFF00,0,0,0,0);
										/* receive all incoming data */
			  SCI_Rx();				
		  }
		  else 
		  { 
		                /* erase reset vectors */
			  LaunchFlashCommand(2, ERASE_P_FLASH_SECTOR, 0x7F, 0xFF00,0,0,0,0);
										/* receive all incoming data */
		    CAN_Rx();
		  }
    }
}



/******************************************************************************
Function Name  : SCI_Rx
Engineer       : b06321	
Date           : 01/11/2006
Parameters     : NONE
Returns        : NONE
Notes          : Downloads code from SCI module and stores it in flash in strict sequence.
						     Calculates download checksum and compares it one sent by the host.
					       
					       ***Assumes flash has already been erased***	
					       ***Data to be written must start on writable flash phrase***

******************************************************************************/
void SCI_Rx(void)
{
	/* LOCAL VARIABLES */
	int 	byteCount;							  /* used to keep count of DATA bytes being downloaded */
	tU08		checkSumTotal = 0;			/* bootloader calculated checksum: Modulo 256 addition of downloaded data & address */
	tU08    globalPage;             /* used to store the page number to which downloaded data is stored */
	tREG16	startAddress;						/* used to control where downloaded data is stored and as start vector after download */
  int i;                          /* general counter */
	int	*__far addressPtr;          /* used to control where each downloaded data byte is stored */
	unsigned char S_lineLength = 0x00;  /* used to store the length of the S-record line in bytes */
	char    byteCounter = 0;            /* used to count number of bytes in current array */
	char nextSCI_Message = SCI_LINE_LENGTH; /* used to control how sequential SCI data is treated */
	unsigned int *dataPtr;          /* used to point to elements of array */
	tU08 receivedPackets[100];      /* used to store 8bytes of data for writing to flash */
  
  for (i=0;i<=99;i++)    /* setting up array */
     receivedPackets[i] = 0xFF;
    
  
  /* MAIN CODE */
  while (1)
	{
                                  /* if SCI has received a byte */
		if (SCI_Ptr->scisr1.bit.rdrff == 1)
		{
		  SCI_Ptr->scisr1.byte = SCI_Ptr->scisr1.byte;  /* Read to clear RDRF flag. The write has no effect */ 
			switch (nextSCI_Message)
			{
			   case SCI_LINE_LENGTH:
			      S_lineLength =  SCI_Ptr->scidrl.byte;	/* store the length of the S-record line */
			      if (S_lineLength == 0x00)
			      {
			            /* transmission complete: reset chip to execute device  */
			            /* this is done by writing to an illegal memory address */  
			            *((unsigned char * far)0x400000) = 0;
			      }
			      else
			      {
			         checkSumTotal = S_lineLength; 
			         nextSCI_Message = SCI_START_GPAGE;
			         byteCount = S_lineLength - 4; /* number of data receives (removing address(3) + checksum (1) = 4)*/
			      }
			      break;
			      
				case SCI_START_GPAGE:
					globalPage = SCI_Ptr->scidrl.byte;  /* store data received as global page of the start address pointer */
					checkSumTotal += SCI_Ptr->scidrl.byte;  /* add it to the checksum */
					nextSCI_Message = SCI_START_ADDR_MSB; /* ensure next data is recognised as the msb of the start address pointer */
					break;
				
				case SCI_START_ADDR_MSB:
					startAddress.byte.msb.byte = SCI_Ptr->scidrl.byte;	/* store data received as msb of the start address pointer */
					checkSumTotal += startAddress.byte.msb.byte;  /* initialize the checksum with first byte */
					nextSCI_Message = SCI_START_ADDR_LSB; /* ensure next data is recognised as the lsb of the start address pointer */
					break;
				
				case SCI_START_ADDR_LSB:
					startAddress.byte.lsb.byte = SCI_Ptr->scidrl.byte;	/* store data received as lsb of the address pointer */
					checkSumTotal += startAddress.byte.lsb.byte;  /* add it to the checksum */
					WP_PAGE(addressPtr) = globalPage; /* initialise the globalpage of the start address */
					WP_ADDR(addressPtr) = startAddress.word;  /* initialise the download address pointer with the start address */
					nextSCI_Message = SCI_DATA_BYTE;  /* ensure next data is recognised as the msb of the data byte count */
					break;
				
				case SCI_DATA_BYTE:					                     
					          /* download data byte into RAM */
					receivedPackets[byteCounter] = SCI_Ptr->scidrl.byte;  /* add data to array */
					checkSumTotal += receivedPackets[byteCounter];  /* add it to the checksum */
					byteCounter++;  /* add to number of bytes in array */
					if (byteCounter == byteCount)
					   nextSCI_Message = SCI_CHECKSUM_BYTE;
					break;

				case SCI_CHECKSUM_BYTE:
					checkSumTotal = 255 - checkSumTotal; /* Taking one's complement */
					if (checkSumTotal == SCI_Ptr->scidrl.byte)
					{																	
						   /* line downloaded successfuly, acknowledge then load to flash */
						SCI_Ptr->scicr2.bit.te = 1;							
						SCI_Ptr->scidrl.byte = CHECKSUM_OK;
						FTM.fprot.byte = 0xFF;              /* no protection */
											         /* write to flash */
					  dataPtr = (int *)receivedPackets;
					  while (byteCounter > 0)
					  {
					    LaunchFlashCommand(6 , PROGRAM_P_FLASH, WP_PAGE(addressPtr), WP_ADDR(addressPtr), *dataPtr++, *dataPtr++, *dataPtr++, *dataPtr++);
					    FP_ADD(addressPtr, 8);/* update address to download location of next block */ 
					    byteCounter = byteCounter - 8;
					  }																					
					}																	
					else 
					{																	
					    /* checksum incorrect, notify then prepare to re-receive */
					  SCI_Ptr->scicr2.bit.te = 1;							
						SCI_Ptr->scidrl.byte = CHECKSUM_BAD;
					}
					nextSCI_Message = SCI_LINE_LENGTH;
					         /* resetting values */
					checkSumTotal = 0;
					byteCounter = 0;
					for (i=0;i<=99;i++)    /* Resetting array */
                  receivedPackets[i] = 0xFF;
					break;														

				default:
					break;
			}
		}			
	}	
}



/******************************************************************************
Function Name  : CAN_Rx
Engineer       : b06321	
Date           : 01/11/2006
Parameters     : NONE
Returns        : NONE
Notes          : Downloads code from CAN module.
						     Calculates download checksum and compares it to the one sent
						     by the host. Each time an address pointer is sent the 
						     checksum is re-initialized.
						     Jumps to downloaded code when instructed by the host.
						
						     ***Assumes flash has already been erased***	
					       ***Data to be written must start on writable flash phrase***	
					       
******************************************************************************/
void CAN_Rx(void)
{
	/* LOCAL VARIABLES */
	tU08		txSelect;						/* used when selecting transmit buffer  */
	union 
	{
	   int 		*__far Ptr;
	   struct
	   {
	      tU08 global;
	      tU08 msb;
	      tU08 lsb;
	   }byte;
	}addressPtr;
	tU08		checkSumTotal = 0;	/* boot loader calculated checksum modulo 256 addition of downloaded data & address */
	tU08		receivedCheckSum;		/* host calculated checksum which gets compared to the boot loader calculated value */
  tS08 		loopCounter;				/* used to count the number of bytes to download from a code data message */
  unsigned int *dataPtr;      /* used to point to elements of array */
  int byteCounter = 0;        /* number of the current field in the receivedPackets array */
  tU08 receivedPackets[100];  /* used to store 8bytes of data for writing to flash */

  for (loopCounter=0;loopCounter<=99;loopCounter++) /* setting up array */
    receivedPackets[loopCounter] = 0xFF;

  
  /* MAIN CODE */
	while (1)
	{								/* wait for new CAN message to arrive */
		while (CAN_Ptr->canrflg.bit.rxf == 0){}										
									/* if data download message received */
		if (CAN_Ptr->rxbuf.id.w[0] == CAN_LOAD_DATA_ID)			
		{											
      for (loopCounter = 0; loopCounter < (CAN_Ptr->rxbuf.dlr.byte); loopCounter++)
      {
        receivedPackets[byteCounter] = CAN_Ptr->rxbuf.dsr[loopCounter];
      	checkSumTotal += receivedPackets[byteCounter];
      	byteCounter++;			
      }
   	} 					  /* if address pointer message received*/
		else if (CAN_Ptr->rxbuf.id.w[0] == CAN_ADDRESS_POINTER_ID)
		{
		 											/* set address pointer where to load data */
			addressPtr.byte.global = CAN_Ptr->rxbuf.dsr[0]; /* initialise the globalpage of the start address */
			addressPtr.byte.msb = CAN_Ptr->rxbuf.dsr[1];
			addressPtr.byte.lsb = CAN_Ptr->rxbuf.dsr[2];
													/* checksum value is always re-initialized on receipt of */
													/* an address pointer. Allows multiple instances of data download  */
			checkSumTotal = addressPtr.byte.global; 
			checkSumTotal += addressPtr.byte.msb;
			checkSumTotal += addressPtr.byte.lsb;
    }											/* if checksum value message received */		
		else if (CAN_Ptr->rxbuf.id.w[0] == CAN_CHECKSUM_VALUE_ID)	
		{
		  receivedCheckSum = CAN_Ptr->rxbuf.dsr[0];
													/* which tx buffer is empty */
			txSelect = CAN_Ptr->cantflg.byte;	
													/* select tx0 buffer for transmission */
			CAN_Ptr->cantbsel.byte = txSelect;		
			CAN_Ptr->txbuf.id.w[0] = CAN_CHECKSUM_STATUS_ID;
			CAN_Ptr->txbuf.dlr.byte = 0x2;
		                      /* send appropriate checksum response message */
			checkSumTotal = 255 - checkSumTotal;
			if (receivedCheckSum == checkSumTotal)
			{
				CAN_Ptr->txbuf.dsr[0] = CHECKSUM_OK;
				                  /* Write the received s-record line to flash */
				FTM.fprot.byte = 0xFF;  /* no protection */
											    /* write to flash */
				dataPtr = (int *)receivedPackets;
				while (byteCounter > 0)
				{
				  LaunchFlashCommand(6 , PROGRAM_P_FLASH, WP_PAGE(addressPtr), WP_ADDR(addressPtr), *dataPtr++, *dataPtr++, *dataPtr++, *dataPtr++);
					FP_ADD(addressPtr, 8);/* update address to download location of next block */ 
				  byteCounter = byteCounter - 8;
			  }			                      
			}
			else
			{
				CAN_Ptr->txbuf.dsr[0] = CHECKSUM_BAD;
			}
			        /* Reset variable for next line / re-transmitted line */
		  checkSumTotal = 0;
		  byteCounter = 0;
			addressPtr.Ptr = (tU08 *)0;
			for (loopCounter=0;loopCounter<=99;loopCounter++)    /* resetting the array */
            receivedPackets[loopCounter] = 0xFF;	
			
			CAN_Ptr->txbuf.dsr[1] = checkSumTotal;
								  /* schedule buffer for transmission */
			CAN_Ptr->cantflg.byte = txSelect;    
	    }						/* if execute code message received */
		  else if (CAN_Ptr->rxbuf.id.w[0] == CAN_EXECUTE_ID)
		  {
		        /* reset chip to execute device */
			      /* this is done by writing to an illegal memory address */  
		    *((unsigned char * far)0x400000) = 0; 										
		  } 	
		      /* clear receive buffer full flag */
		  CAN_Ptr->canrflg.bit.rxf = 1;		
	}
}


/******************************************************************************
Function Name	:	Delay
Engineer		   :	r32151
Date			   :	02/06/00
Parameters		:	unsigned int delayTime
Returns			:	NONE
Notes			   :	Simple software delay dependent on CPU clock frequency and
					   compile strategy 
******************************************************************************/
void Delay(unsigned int delayTime)					
{
    unsigned int x;						 /*outer loop counter */
  	char y;									 /*inner loop counter */

  	for (x=0; x<delayTime; x++)
  	{	
  		for (y=0; y<100; y++){} 
	  }
}