/******************************************************************************
Copyright (c) Freescale 2005
File Name : $RCSfile: S12X LRAE_Bootloader.c,v $
Engineer : $Author: r32151 $
Location : EKB
Date Created : 01/06/2005
Current Revision : $Revision: 0.2 $
Notes :	Main code for the HCS12X Generic bootloader. The bootloader scans the 
		  	specified CAN and SCI modules for communications at a defined set of 
		  	communications frequencies allowing for oscillator rates of 4, 8 and 
		  	16MHz.
		  	
		  	"HCS12 Load RAM and Execute Bootloader User Guide", Application Note 
		  	AN2546 is available from http://e-www.Freescale.com. This contains 
		  	additional information on the use of the bootloader. 

		  	This is the only source file in the LRAE project. It was decided not
		  	to use a Start.C routine in order to save space and as the bootloader 
		  	code is simple and contains few variables so we can initialize
		  	them in the main code. This is important to understand if modifying 
		  	this code - global variables cannot be initialized during declaration.
		  	To allow the code to be easily reviewed and as no functions get called 
		  	from outside this code there is no main.h and the function prototypes 
		  	are defined below.
		  	
		  	The code strategy is to use global pointers to access a variable 
		  	number of CAN and SCI modules using generic functions. Use of RAM is
		  	minimized in order to maximize the RAM available for download.
		  	
		  	LRAE Top Level Flow 

				Jump to application in flash if $C000-$C001 is programmed
				Configure MCU resources

				LRAE initialize:
					For each CAN module:
						If CAN physical interface detected, 
							configure for initial bit rate
						If CAN module synchronizes to bus (= active), 
							put CAN into sleep mode
					For each SCI module:
						Configure for first SCI baud rate
						Enable Rx 

				Main Loop:
					For each active CAN module
						If the CAN module is awake
							If address pointer message has been received, 
								complete download from CAN module and jump to 
								downloaded code
							If data not received and timeout completed, 
								change module to next bit rate
							If data not received but timeout not completed, 
								increment module timeout counter
					For each SCI module
						If data byte has been received
							If data is valid synchronization byte,
								transmit acknowledge byte, complete download from SCI
								and jump to downloaded code
							If data byte is not the synchronization byte (twice),
								change module to next baud rate
				Loop back to Main Loop

Revision History:
Rev 0.2:  Ported software to S12XE
          Changes made to ensure compatibility with secondary bootloader

******************************************************************************/

/************************* Project Include Files *****************************/

#include <hidef.h>      /* common defines and macros */

/************************* #Defines ******************************************/

#define APPLICATION_START  		0xC000   /* Flash application start vector - if this location is programmed the */
														/* the bootloader transfers execution to the address it contains instead of */
														/* performing a code download */
#define ERASED							0xFFFF	/* State of address $C000-$C001 if download is to be carried out */

#define CHECKSUM_OK 					0x80		/* Valid checksum response - transmitted if calculated checksum matches received value */
#define CHECKSUM_BAD					0x01		/* Bad checksum response */ 

#define SCI_SYNC_MSG 				0x55		/* SCI synchronisation value - sent by host until SCI is synchronised */
#define SCI_ACK_RESPONSE			0xAA		/* SCI response value - transmitted when synchronised */
#define SCI_START_ADDR_MSB			1			/* SCI Rx state machine control */
#define SCI_START_ADDR_LSB			2			/* SCI Rx state machine control */
#define SCI_DATA_COUNT_MSB			3			/* SCI Rx state machine control */
#define SCI_DATA_COUNT_LSB			4			/* SCI Rx state machine control */
#define SCI_DATA_BYTE				5			/* SCI Rx state machine control */
#define SCI_CHECKSUM_BYTE			6			/* SCI Rx state machine control */

#define CAN_ACK_WINDOW 				400		/* Timeout limit for controlling period between changing the CAN bus data rate      */
														/* 400 equates to ~20ms with a 4MHz clock so worst case sync delay would be for CAN */
														/* Config4@ 11 * 20ms = 220ms. At 16MHz the window for each bus speed is ~ 5ms. A   */
														/* two byte data message (such as Set Address Pointer) at 50Kbit/s is 80 bits max.  */
														/* = 1.6ms. 5ms allows for 3 repeats of the message to ensure efficient             */
														/* synchronization.*/
#define CAN_SYNC_WINDOW				2000		/* Nominal timeout value for limiting the CAN synchronisation window */
#define CAN_EXECUTE_ID				0x0200	/* CAN ID 0x010 (<< 5) - 11 bit ID on MSCAN is left justified, hence the shifts */
#define CAN_ADDRESS_POINTER_ID 	0x0400	/* CAN ID 0x200 (<< 5) */
#define CAN_LOAD_DATA_ID			0x0800	/* CAN ID 0x040 (<< 5) */
#define CAN_CHECKSUM_VALUE_ID		0x1000	/* CAN ID 0x080 (<< 5) */
#define CAN_CHECKSUM_STATUS_ID  	0xC000	/* CAN ID 0x600 (<< 5) */
#define ACCEPT_ALL_MASK				0xFFFFFFFFL	/* CAN Acceptance mask to accept all CAN identifiers */  

/* Device selection is in "projectglobals.h" */
/* SCI0_PRESENT is implicit as there is always expected to be an SCI0 module */


#define XEx100_M22E
													/* Devices with 3 CAN & 2 SCI modules */
#ifdef XEx100_M22E    				      /* device is Ex100            */		
#include "per_XEx100_M22E.h"
	#define CAN0_PRESENT
	#define CAN1_PRESENT
	#define CAN4_PRESENT
	#define SCI1_PRESENT
	#define SCI2_PRESENT
#endif

/************************* Global Variables **********************************/

/* GLOBAL VARIABLES CANNOT BE INITIALIZED HERE AS THERE IS NO STARTUP CODE */  

#ifdef CAN0_PRESENT
volatile tMSCAN 	*CAN_Ptr;							/* Global pointer used for indirect accessing of the different CAN modules */
#endif /* CAN0_PRESENT */

volatile tSCI 		*SCI_Ptr;							/* Global pointer used for indirect accessing of the different SCI modules */

tU08		SCI0_SpeedIndex;					/* Used to select SCI0 prescaler. Bit7 used to count 2 received bytes at each bus speed */
tU08		SCI1_SpeedIndex;					/* Used to select SCI1 prescaler. Bit7 used to count 2 received bytes at each bus speed */
tU08		SCI2_SpeedIndex;					/* Used to select SCI1 prescaler. Bit7 used to count 2 received bytes at each bus speed */

tU08		*SCI_SpeedIndexPtr; 		      /* Pointer to indices above */

union		/* Flags byte - individual bits are assigned as tokens to flag */
{			/* which CAN modules synchronise with external CAN busses.     */
	tU08	byte;
	struct
	{
		tU08 CAN0_Active 			:1;	/* Used to flag that CAN0 is available and synchronised */
		tU08 CAN1_Active			:1;	/* Used to flag that CAN1 is available and synchronised */
		tU08 CAN4_Active  		:1;	/* Used to flag that CAN4 is available and synchronised */
      tU08 SCI_or_CAN         :1;
      tU08		     				:4;	/* Not used */
 	}bit;
}CanFlags;	

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

/* refer to Application Note AN2564 for details of the bus timing rates */ 

                                       /* CAN port speed configuration options */
#ifdef CAN0_PRESENT 
const tU08 CanBitTimingBtr[][2] =   {  {0x40, 0x14}, /* #1  */
									                     {0x40, 0x1C}, /* #2  */
                                       {0x41, 0x1C}, /* #3  */
                                       {0x43, 0x1C}, /* #4  */
                                       {0x47, 0x9C}, /* #5  */
                                       {0x43, 0x98}, /* #6  */
                                       {0x47, 0x98}, /* #7  */
                                       {0x4F, 0x98}, /* #8  */
                                       {0x44, 0x9C}, /* #9  */
                                       {0x49, 0x9C}, /* #10 */
                                       {0x53, 0x9C}  /* #11 */
};
#endif /* CAN0_PRESENT */

                                       /* SCI port baud rate prescaler options */
const tU08 SCI_Prescaler[] = { 1,      /* #1 */
										 2,      /* #2 */
										 4,      /* #3 */
										 9,      /* #4 */
										 7,      /* #5 */
										13,      /* #6 */
										26,      /* #7 */
										52       /* #8 */
};  

volatile const tU16 FlashSecurity @ 0xFF0E = 0xFFFE ;	 /* ensure that the device remains unsecured by the s-record */
																				 /* Not actively used by the LRAE application. */


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

/* The following two macros allow non-typical use of registers in the CAN Tx data register field as variables used	*/
/* in establishing CAN communications which are local to each CAN module and map in and out via the CAN_Ptr value.  	*/ 
/* This is viable as there is no CAN transmit activity while these variables are in use.										*/
/* They are implemented as macros to help readability and to avoid confusion due to their non-typical use.           */
 
													/* This allows utilisation of a transmit data register byte as an index 	  	*/
													/* for tracking the current CAN bus bit rate setting during the CAN bus speed */
													/* matching process. */
#define CAN_SPEED_OPTION (CAN_Ptr->txbuf.dsr[0])
													/* This allows utilisation of a transmit ID register word as a timeout      	*/
													/* counter to control the window for each specific CAN bus speed to attempt  	*/
													/* to synchronise during the CAN bus speed matching process. */
#define CAN_TIMEOUT_COUNTER (CAN_Ptr->txbuf.id.w[0])

/************************* Function Prototypes ********************************/

tU08 InitCAN(void);					/* returns PASS or FAIL */
void CAN_BusSpeed(void);
void CheckCAN_Activity(void);
void CAN_Rx(void);
void CheckSCI_Activity(void);
void SCI_BusSpeed(void);
void SCI_Rx(void);
void Initialize(void);
void main(void);

/******************************************************************************
Function Name	:	main
Engineer			:	Joanne McNamee 
Date				:	Dec 17, 2002

Parameters		:	none
Returns			:	none
Globals			:	modifies CAN_Ptr
						modifies SCI_Ptr
						modifies SCI_SpeedIndexPtr
						
Notes				:	1) Tests to see if the application start vector is programmed
						and jumps to code in flash if it is - Exit from LRAE.
						2) Initializes the LRAE resources and communications modules.
						3) Main loop scans active communications modules until one 
						has synchronized and is ready to download. At this point the 
						appropriate CAN_Rx() or SCI_Rx() routine takes over - the 
						main loop is never re-entered.
Modified:
Engineer				Date				Reason
Joanne McNamee		Aug 31, 2003	New project modifications to existing 
											software.
Martyn Gallop		Nov 21, 2003	Code re-write. Conversion to pointer based 
											strategy.
Grant More 			Dec 01, 2003	Corrected vector to application start from 
											load immediate in order to jump to the 
											contents of APPLICATION_START	as opposed  
											to the address of APPLICATION_START.
******************************************************************************/
void 
main( void )
{
													/* if the application start vector is erased execute the LRAE routines */
													/* else jump to the application programmed in flash */
	if (*(tU16 *)APPLICATION_START == ERASED)
	{												
													/* ensure interrupts are disabled */
		asm sei;					
		
      /* initialise the system clock. Fbus = 8MHz. Assumes 4MHz Oscillator */
   CRG.synr.byte = 0x3;         /* configure the PLL */
   CRG.refdv.byte = 0x40;
   CRG.postdiv.byte = 0x1;
   while(!CRG.crgflg.bit.lock)	/* wait for PLL to lock */
   {
   }
	 CRG.clksel.bit.pllsel = 1; /*select the PLL as the clock source */
		
		asm lds #$4000;	
													/* continue with the LRAE routines */
		Initialize();
		while (1)
		{
			#ifdef CAN0_PRESENT
				if(CanFlags.bit.CAN0_Active == 1)				/* only if CAN0 is enabled and synchronised */
				{
					CAN_Ptr = &CAN0;
					CheckCAN_Activity();								/* check to see if anything received on CAN0 */
				}
			#endif /* end if for CAN0 */

			#ifdef CAN1_PRESENT
				if(CanFlags.bit.CAN1_Active == 1)				/* only if CAN1 is enabled and synchronised */
				{
					CAN_Ptr = &CAN1;
					CheckCAN_Activity();								/* check to see if anything received on CAN1 */
				}
			#endif	/* end if for CAN1 */

			#ifdef CAN4_PRESENT
				if(CanFlags.bit.CAN4_Active == 1)				/* only if CAN4 is enabled and synchronised */
				{
					CAN_Ptr = &CAN4;
					CheckCAN_Activity();								/* check to see if anything received on CAN4 */
				}
			#endif  /* end if for CAN4 */

			SCI_Ptr = &SCI0;											/* select SCI0*/
			SCI_SpeedIndexPtr = &SCI0_SpeedIndex;
			CheckSCI_Activity();										/* check to see if anything received */

			#ifdef SCI1_PRESENT
				SCI_Ptr = &SCI1;										/* select SCI1*/
				SCI_SpeedIndexPtr = &SCI1_SpeedIndex;
				CheckSCI_Activity();									/* check to see if anything received */
			#endif /* end if for SCI1 */

			#ifdef SCI2_PRESENT
				SCI_Ptr = &SCI2;										/* select SCI1*/
				SCI_SpeedIndexPtr = &SCI2_SpeedIndex;
				CheckSCI_Activity();									/* check to see if anything received */
			#endif /* end if for SCI1 */
		}			
	}
	else
	{																						/****************************************/
		asm ldx	APPLICATION_START;								/****** LRAE TRANSFERS EXECUTION TO *****/
		asm jmp	0,x;           										/******* APPLICATION CODE IN FLASH ******/
	}																						/****************************************/
}

/******************************************************************************
Function Name	:	Initialize
Engineer			:	Joanne McNamee 
Date				:	Dec 17, 2002

Parameters		:	none 
Returns			:	none 
Globals			:	initializes CanFlags byte
						modifies CAN_Ptr
						modifies CanFlags bits
						initializes SCI0_SpeedIndex & SCI1_SpeedIndex
						

Notes				:	Attempts to identify CAN interfaces attached to present CAN
						modules by driving the appropriate IO pins (see below).
						If a CAN interface is detected the CAN module is configured 
						for first bit rate.
						If CAN module synchronizes to a CAN bus the CAN will be put
						into sleep mode and a flag is set to show that this CAN 
						module is present and active.
						Configures SCI0 to receive at an initial baud rate.
						If present, configures SCI1 to receive at an initial baud 
						rate.
  
						CAN IO check:
						a) checks that the CAN Tx pin is high (as it should be when 
						pulled up by a CAN physical interface). The Rx pin isn't 
						sampled as it might be being driven by a CAN message already 
						so cannot be predicted.
						b) then drives CAN Tx low to make sure it is not being driven 
						by an external driver - the pin status is read directly using 
						the port input register (an HCS12 feature).
						c) if it did drive low the CAN Rx pin is sampled following a 
						delay to check that it went low also (although this could be
						coincidental).
						d) if it did then the CAN module is initialized and tries to 
						synchronize with the CAN bus.
						This does not 100% guarantee to correctly detect a CAN 
						interface but it is a reasonable test and at least avoids
						enabling the CAN Tx function onto a pin that cannot be driven, 
						avoiding potential output conflict.
Modified:
Engineer				Date				Reason
Joanne McNamee		Aug 31, 2003	New project modifications to existing 
											software.
Martyn Gallop		Nov 21, 2003	Code re-write. Conversion to pointer based 
											strategy. Added CAN port test.
Grant More 			Dec 01, 2003	Corrected CAN pointer assignment to point
											to the appropriate CAN modules.
Martyn Gallop     Dec 05, 2003	Changed writing to the SCI baud to two byte 
                                 writes to ensure correct write sequence.
Martyn Gallop		Dec,08  2003   Increased read delay in CAN port detection 
											to 3.25us. 
******************************************************************************/
void 
Initialize()
{
	#ifdef CAN0_PRESENT
													/* clear all CAN port check flags */
		CanFlags.byte = 0x00;				
													/* Sanity check of CAN I/O ports. The CAN i'face */
													/* has an input pull so with the MCU pin,        */
													/* defaulting to input, should read 1. The CAN   */
													/* interface Rx pin should track the Tx pin.     */
													/* Checked several HS and LS CAN i'face specs to */
													/* ensure that Tx to Rx propagation delay is     */
													/* maximum 2us.											             */

																/* is CAN0 Tx pin = 5V? (CAN interface pull-up attached) */
		if (PTIM.bit.bit1 == 1)  				 
		{														/* if it is : */
			PTM.bit.bit1 = 0;						/* ensure the O/P port data is low */
			DDRM.bit.bit1 = 1;						/* make the CAN0 Tx pin output */
			PTM.bit.bit1 = 0;						/* delay - allow for some external pin capacitance */
			PTM.bit.bit1 = 0;						/* delay */
			PTM.bit.bit1 = 0;						/* delay */
																/* check that the pin was able to be driven low */
			if (PTIM.bit.bit1 == 0)
			{													/* if it was, continue CAN interface analysis */
				PTM.bit.bit1 = 0;					/* delay - allowing for low speed interface propagation */ 
				PTM.bit.bit1 = 0;					/* delay */ 
				if (PTIM.bit.bit0 == 0)			/* now check if the CAN0 Rx pin is also low - read delayed ~3.25us @ 16MHz*/
				{												/* if it is, there may be a CAN transceiver may be present. */ 
																/* initialize the CAN0 module */
					CAN_Ptr = &CAN0;
					if (InitCAN() == PASS) 
					{
						CanFlags.bit.CAN0_Active = 1 ;
					}
				}
			}													
			DDRM.bit.bit1 = 0;						/* reset the CAN0 Tx pin to input. */
																/* This has no effect if CAN0 is initialized */
		}
	#endif /* CAN0_PRESENT */
	
	#ifdef CAN1_PRESENT
																/* is CAN1 Tx pin = 5V? (CAN interface pull-up attached?) */
		if (PTIM.bit.bit3 == 1)  				 
		{														/* if it is : */
			PTM.bit.bit3 = 0;						/* ensure the O/P port data value is low */
			DDRM.bit.bit3 = 1;						/* make the CAN1 Tx pin output */
			PTM.bit.bit3 = 0;						/* delay - allowing for some external pin capacitance */
			PTM.bit.bit3 = 0;						/* delay */
			PTM.bit.bit3 = 0;						/* delay */
																/* check that the pin was able to be driven low */
			if (PTIM.bit.bit3 == 0)
			{													/* if it was, continue CAN1 interface analysis */
				PTM.bit.bit3 = 0;					/* delay - allowing for low speed interface propagation */ 
				PTM.bit.bit3 = 0;					/* delay */ 
				if (PTIM.bit.bit2 == 0)			/* now check if the CAN1 Rx pin is also low - read delayed ~3.25us @ 16MHz*/
				{												/* if it is, there may be a CAN transceiver may be present. */ 
																/* initialize the CAN1 module */
					CAN_Ptr = &CAN1;
					if (InitCAN() == PASS) 
					{
						CanFlags.bit.CAN1_Active = 1 ;
					}
				}
			}													
			DDRM.bit.bit3 = 0;						/* reset the CAN1 Tx pin to input */
																/* This has no effect if CAN1 is initialized */
		}
	#endif /* CAN1_PRESENT */
	
	#ifdef CAN4_PRESENT
																/* is CAN4 Tx pin = 5V? (CAN interface pull-up attached?) */
		if (PTIJ.bit.bit7 == 1)  				 
		{														/* if it is : */
			PTJ.bit.bit7 = 0;						/* ensure the O/P port data value is low */
			DDRJ.bit.bit7 = 1;						/* make the CAN4 Tx pin output */
			PTJ.bit.bit7 = 0;						/* delay - allowing for some external pin capacitance */
			PTJ.bit.bit7 = 0;						/* delay */
			PTJ.bit.bit7 = 0;						/* delay */
																/* check that the pin was able to be driven low */
			if (PTIJ.bit.bit7 == 0)
			{													/* if it was, continue CAN4 interface analysis */
				PTJ.bit.bit7 = 0;					/* delay - allowing for low speed interface propagation */ 
				PTJ.bit.bit7 = 0;					/* delay */ 
				if (PTIJ.bit.bit6 == 0)			/* now check if the CAN4 Rx pin is also low - read delayed ~3.25us @ 16MHz*/
				{												/* if it is, there may be a CAN transceiver present. */ 
																/* initialize the CAN4 module */
					CAN_Ptr = &CAN4;
					if (InitCAN() == PASS) 
					{
						CanFlags.bit.CAN4_Active = 1 ;
					}
				}
			}
			DDRJ.bit.bit7 = 0;						/* reset the CAN4 Tx pin to input */
																/* This has no effect if CAN4 is initialized */
		}
	#endif /* CAN4_PRESENT */
	
													/* set up first SCI bus speed */
		SCI0_SpeedIndex = 0;
													/* load the next baud rate prescaler */
													/* msb is always zero for the LRAE   */
	   SCI0.scibdh.byte = 0;
   	SCI0.scibdl.byte = SCI_Prescaler[0];
													/* enable SCI0 to receive */
		SCI0.scicr2.bit.re = 1;						

	#ifdef SCI1_PRESENT
													/* set up first SCI bus speed */
		SCI1_SpeedIndex = 0;
	   SCI1.scibdh.byte = 0;
	   SCI1.scibdl.byte = SCI_Prescaler[0];
													/* enable SCI1 to receive */
		SCI1.scicr2.bit.re = 1;						
	#endif	/* SCI1_PRESENT */

	#ifdef SCI2_PRESENT
													/* set up first SCI bus speed */
		SCI2_SpeedIndex = 0;
	   SCI2.scibdh.byte = 0;
	   SCI2.scibdl.byte = SCI_Prescaler[0];
													/* enable SCI2 to receive */
		SCI2.scicr2.bit.re = 1;						
	#endif	/* SCI1_PRESENT */
}

/******************************************************************************
Function Name	:	CheckSCI_Activity
Engineer			:	Martyn Gallop 
Date				:	Nov 21, 2003

Parameters		:	none
Returns			:	none
Globals			:  Requires SCI_Ptr be initialized
						Modifies bit 7 of SCIx_SpeedIndex

Notes				:	Checks to see if a byte of data has been received on the SCI.
						If the byte is a valid synchronisation byte, transmits an
						acknowledge byte and calls the SCI_Rx() download routine - 
						which never returns.
						Otherwise, if two incorrectly received bytes, changes baud 
						rate to the next value.
												
						When the baud rate is changed it can cause a currently 
						receiving byte to be seen as incorrect data, even though the 
						baud rate may then actually be correct. Forcing this  
						routine to be called twice before each change of baud rate  
						ensures that a full character is received at each baud rate 
						setting. 
						Bit 7 of the SCI speed index pointer is used as a toggling
						flag to enable a change of bus speed only if an incorrect 
						data byte has been received twice (and this routine has been 
						called a second time). 
Modified:
Engineer				Date				Reason
Daniel Malik      Nov 27, 2003   Modified to use BIT7 to gate change of bus
                                 the speed only every other time this routine 
                                 is called.
******************************************************************************/
void
CheckSCI_Activity(void)
{												/* if byte of data received */
	if (SCI_Ptr->scisr1.bit.rdrff == 1)
	{
												/* if message received is the sync message */
		if (SCI_Ptr->scidrl.byte == SCI_SYNC_MSG)		
		{
												/* enable Tx */
			SCI_Ptr->scicr2.bit.te = 1;
												/* wait for transmit buffer empty status */
			while (SCI_Ptr->scisr1.bit.tdre == 0)
			{}	
												/* send acknowledge message */
			SCI_Ptr->scidrl.byte = SCI_ACK_RESPONSE;
												/* then receive all incoming data */
			SCI_Rx();				
		}
		else 
		{									   /* if first incorrect character is received */
		   if ((*SCI_SpeedIndexPtr & 0x80) == 0)  
		   {								   /* set incorrect character flag */
			   *SCI_SpeedIndexPtr |= BIT7;
		   }                          /* if a second incorrect character is received */
		   else								
		   {									/* clear incorrect character flag */
            *SCI_SpeedIndexPtr &= (~(BIT7));   
												/* and try a different bus speed - note BIT7 is always cleared before */  
												/* calling SCI_BusSpeed so as not to affect the speed index value */
		      SCI_BusSpeed();
		   } 				
		}
	}
}

/******************************************************************************
Function Name	:	SCI_BusSpeed
Engineer			:	Joanne McNamee 
Date				:	Dec 17, 2002

Parameters		:	none
Returns			:	none
Globals			:  Requires SCI_SpeedIndexPtr be initialized
						Requires SCI_Ptr be initialized
						Requires SCIx_SpeedIndex be initialized
						Modifies SCIx_SpeedIndex
						
Notes				:	Changes the SCI bus speed to the next one in	the const array 
						SCI_Prescaler.
Modified:
Engineer				Date				Reason
Joanne McNamee		Aug 31, 2003	New project modifications to existing 
											software.	
Daniel Malik		Nov 21, 2003	Code re-write. Conversion to pointer based 
											strategy.
Martyn Gallop     Dec 05, 2003	Changed writing to the SCI baud to two byte 
                                 writes to ensure correct write sequence.
******************************************************************************/
void 
SCI_BusSpeed()
{
													/* increment the SCI speed selector index */
	(*SCI_SpeedIndexPtr)++;
													/* allow wrap around of the index value */
	if ((*SCI_SpeedIndexPtr) >= sizeof(SCI_Prescaler)) 
	{												
		*SCI_SpeedIndexPtr = 0;	         
	}
													/* configure next SCI baud rate */
													/* msb is always zero for the LRAE   */
	SCI_Ptr->scibdh.byte = 0;
	SCI_Ptr->scibdl.byte = SCI_Prescaler[*SCI_SpeedIndexPtr];
}

/******************************************************************************
Function Name	:	SCI_Rx
Engineer			:	Joanne McNamee 
Date				:	17 dec 2002

Parameters		:	none
Returns			:	none
Globals			:  Requires SCI_Ptr be initialized

Notes				:	Downloads code from SCI module in strict sequence.
						Calculates download checksum and compares it one sent by the
						host.
						If the checksum is OK, transfers execution to downloaded code. 
						Otherwise, halt until reset.
Modified:
Engineer				Date				Reason
Joanne McNamee		Aug 31, 2003	New project modifications to existing 
											software
Martyn Gallop		Nov 21, 2003	Code re-write. Conversion to pointer based 
											strategy.
Grant More			Nov 26, 2003	Checksum calculation changed to sum the 
											received copy of each data byte instead of 
											reading the Rx buffer again to ensure that 
											the next byte Rx isn't prematurely read.
Martyn Gallop		Nov 26, 2003	Changed most variables from global to local.
											Change global NextSCI_Message to local
											nextSCI_Message and initialized it here
											prior to entering the while loop.
											Changed read to clear rdrf flag so as not
											to require a temp variable.
											Only clears the RDRF flag in one location
											once detected.
											Changed flow control from enum to #defines
											to allow a char test to be used.
Martyn Gallop		Dec 11, 2003	Re-instated data byte read instruction.
Daniel Mckenna  Oct 04, 2006  Added SCI_or_CAN variable to denote to secondary 
                      bootloader which interface is in use. 
******************************************************************************/
void 
SCI_Rx(void)
{
	tREG16 	byteCount;							/* used to keep count of data bytes being downloaded */
	tU08		checkSumTotal;						/* boot loader calculated checksum modulo 256 addition of downloaded data & address */
	tREG16	startAddress;						/* used to control where downloaded data is stored and as start vector after download */

	tU08 		*addressPtr;                  /* used to control where each downloaded data byte is stored */
	tU16 		loopCounter = 0;					/* used to count the number of bytes downloaded */

	char 		nextSCI_Message = SCI_START_ADDR_MSB;     /* used to control how sequential SCI data is treated */

	CanFlags.bit.SCI_or_CAN = 0;   /* denotes to secondary bootloader that SCI interface is in use */
	
	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_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 */
					addressPtr = (tU08 *)startAddress.word;				/* initialise the download address pointer with the start address */
					nextSCI_Message = SCI_DATA_COUNT_MSB;					/* ensure next data is recognised as the msb of the data byte count */
					break;
				
				case SCI_DATA_COUNT_MSB:
					byteCount.byte.msb.byte = SCI_Ptr->scidrl.byte;		/* store data received as msb of the data byte count */	
					checkSumTotal += byteCount.byte.msb.byte;				/* add it to the checksum */
					nextSCI_Message = SCI_DATA_COUNT_LSB;					/* ensure next data is recognised as the lsb of the data byte count */
					break;
				
				case SCI_DATA_COUNT_LSB:
					byteCount.byte.lsb.byte = SCI_Ptr->scidrl.byte;		/* store data received as lsb of the data byte count */
					checkSumTotal += byteCount.byte.lsb.byte;				/* add it to the checksum */
					nextSCI_Message = SCI_DATA_BYTE;							/* ensure next data is recognised as a code data byte */
					break;
				
				case SCI_DATA_BYTE:					                     /* download data byte into RAM */
					*addressPtr = SCI_Ptr->scidrl.byte;				   	/*	load data into RAM */
					checkSumTotal += *addressPtr;								/* add it to the checksum */
					loopCounter++;													/* update data count */
					addressPtr++;													/* point to next download location */
																						/* test to see if all code data downloaded */
					if (loopCounter == byteCount.word) 
					{																	/* if it is, ensure next data is recognised as a host checksum value */ 
						nextSCI_Message = SCI_CHECKSUM_BYTE;
					}
					break;

				case SCI_CHECKSUM_BYTE:
					if (checkSumTotal == SCI_Ptr->scidrl.byte)
					{																	      /*********** CORRECT CHECKSUM ***********/
						SCI_Ptr->scicr2.bit.te = 1;						/******** send back checksum ack ********/
						SCI_Ptr->scidrl.byte = CHECKSUM_OK;		/******* LRAE PASSES EXECUTION TO *******/	
						asm ldx	startAddress.word;						/*********** DOWNLOADED CODE ************/
						asm jmp	0,x;												  /****************************************/
					}																	
					else 
					{																	
						while(1)														  /********* INCORRECT CHECKSUM ***********/
						{}																    /****** LRAE STOPS WILL HALT HERE *******/
					}																	      /* do nothing - wait for external reset */
					break;															    /****************************************/
				
				default:
					break;
			}
		}			
	}	
}

/******************************************************************************/
#ifdef CAN0_PRESENT 	/* Routines after this point are only compiled for  		*/     
							/* devices with CAN modules present.                     */
							/* If there are any CAN modules at all on a device CAN0  */
							/* will be present.									          	*/  

/******************************************************************************
Function Name	:	InitCAN
Engineer			:	Martyn Gallop 
Date				:	17 dec 2002

Parameters		:	none
Returns			:	tU08  PASS / FAIL 
Globals			:  Requires CAN_Ptr be initialized
						Initializes CAN_SPEED_OPTION

Notes				:	Initializes a CAN module for first bit rate option.
						Checks to see if CAN module synchronizes with the bus.
						If it does, puts the CAN module into sleep mode.
						Returns status indicating if CAN module is to be considered
						active or not.
Modified:
Engineer				Date				Reason
Martyn Gallop		Nov 21, 2003	Code re-write. Conversion to pointer based 
											strategy.
Grant More 			Dec 01, 2003	Added explicit else for the FAIL case.
Martyn Gallop     Dec 05, 2003   Moved CAN intialization routine from 
                                 CAN_BusSpeed() to InitCAN() so the CAN speed 
                                 options pointer gets initialized correctly.
******************************************************************************/
tU08							
InitCAN(void)
{
	                        /* used to set a time out limit when waiting for module to synchronize with a CAN bus */
	int syncTimeOut = 0;
													/* request CAN initialisation mode */
	CAN_Ptr->canctl0.bit.initrq = 1;			
													/* wait for initialisation acknowledge */
	while (CAN_Ptr->canctl1.bit.initak == 0)
	{}		
													/* enable the CAN module, set PLL as CLK source */	
	CAN_Ptr->canctl1.byte = CANE | CLKSRC;				
													/* set up CAN to receive all messages */	
	CAN_Ptr->canid[0].canidmr.l = ACCEPT_ALL_MASK;	
                                       /* configure for initial bit rate */
	CAN_Ptr->canbtr0.byte = CanBitTimingBtr[0][0];
	CAN_Ptr->canbtr1.byte = CanBitTimingBtr[0][1];
													/* leave initialisation mode */
	CAN_Ptr->canctl0.bit.initrq = 0;			
													/* wait to enter normal mode */
	while (CAN_Ptr->canctl1.bit.initak == 1)
	{}											
													/* need to do this to make the CAN_SPEED_OPTION variable visible  */
													/* in the map as it is actually using a CAN Tx data register.     */
	CAN_Ptr->cantbsel.byte = 0x01;		
													/* initialize CAN bus speed index register to 2nd bit rate option */ 
	CAN_SPEED_OPTION = 1;
													/* zero the module bus speed timeout register */ 
	CAN_TIMEOUT_COUNTER = 0;
													/* wait for CAN bus sync */
	while  ((CAN_Ptr->canctl0.bit.synch == 0) && (syncTimeOut < CAN_SYNC_WINDOW))
	{
		syncTimeOut++;
	}		 												
													/* put module into sleep mode */
	if (syncTimeOut <  CAN_SYNC_WINDOW)	/* only if synchronised - else module won't enter sleep */
	{
													/* set module to wake up from sleep if there is CAN activity */
		CAN_Ptr->canctl0.bit.wupe = 1;				
													/* request sleep mode */
		CAN_Ptr->canctl0.bit.slprq = 1;					
													/* wait for sleep acknowledge or synchroniazation TimeOut */
		while (CAN_Ptr->canctl1.bit.slpak == 0) 		
		{
		}
		return (tU08)PASS;
	}
	else
	{
		return (tU08)FAIL;
	}
}

/******************************************************************************
Function Name	:	CAN_BusSpeed
Engineer			:	Joanne McNamee 
Date				:	17 dec 2002

Parameters		:	none
Returns			:	none
Globals			:  Requires CAN_Ptr to be initialized
						Requires module CAN_SPEED_OPTION to be initialized
						Modifies module CAN_SPEED_OPTION
						Resets module CAN_TIMEOUT_COUNTER

Notes				:	Changes the CAN bus speed to the next one in	the const array 
						CanBitTimingBtr.
						Resets the CAN module timeout counter. 					

Modified:
Engineer			   Date				Reason
Joanne McNamee		Aug 31, 2003	New project modifications to existing software
Martyn Gallop	   Nov 21, 2003	Code re-write. Conversion to pointer based 
											strategy.
Martyn Gallop	   Nov 26, 2003	Global "temp" changed to local speedOption
Grant More		   Dec 01, 2003	Changed speed option overflow test to use 
											the local copy. Updates the CAN module
											copy with the local copy before exiting.
Martyn Gallop     Dec 05, 2003   Moved CAN intialization routine to 
                                 InitCAN() so the CAN speed options pointer 
                                 is now initialized correctly.
******************************************************************************/
void 
CAN_BusSpeed(void)
{
                                       /* local copy of the module speed option index */
	tU08 speedOption;
													/* need to buffer this because it is not visible in initialisation mode */
	speedOption = CAN_SPEED_OPTION;
													/* request can initialisation mode */
	CAN_Ptr->canctl0.bit.initrq = 1;			
													/* wait for initialisation acknowledge */
	while (CAN_Ptr->canctl1.bit.initak == 0)
	{}		
	                                    /* configure next CAN bit timing */
	CAN_Ptr->canbtr0.byte = CanBitTimingBtr[speedOption][0];
	CAN_Ptr->canbtr1.byte = CanBitTimingBtr[speedOption][1];

													/* leave initialisation mode */
	CAN_Ptr->canctl0.bit.initrq = 0;			
													/* wait to enter normal mode */
	while (CAN_Ptr->canctl1.bit.initak == 1)
	{}											
													/* need to do this to make the CAN_SPEED_OPTION and CAN_TIMEOUT_COUNTER variable visible */ 
													/* in the map again as the CANTBSEL byte is cleared by initialisation mode */
	CAN_Ptr->cantbsel.byte = 0x01;		
													/* allow wrap around of the CAN bus speed selector */
	if (++speedOption >= (sizeof(CanBitTimingBtr)/2)) 
	{
		CAN_SPEED_OPTION = 0;				/* reset the CAN bit rate index pointer */ 
	}
	else
	{										      /* store buffered value back into the CAN module register */
		CAN_SPEED_OPTION = speedOption;
	}												/* zero the CAN module bus speed timeout register */ 
	CAN_TIMEOUT_COUNTER = 0;
}

/******************************************************************************
Function Name	:	CheckCAN_Activity
Engineer			:	Martyn Gallop 
Date				:	17 dec 2002

Parameters		:	none
Returns			:	none
Globals			:  
						Updates selected CAN module CAN_TIMEOUT_COUNTER

Notes				: 	Checks to see if CAN module has exited from sleep due to 
						activity on the bus (CAN module never returns to sleep).
						If no message has been received, update the CAN module 
						timeout counter and change the bit rate if timed out.
						Otherwise, if CAN has successfully received a 
						CAN_ADDRESS_POINTER_ID call the CAN_Rx() download routine -
						which never returns.
Modified:
Engineer				Date				Reason
Joanne McNamee		Aug 31, 2003	Original coding.	
Martyn Gallop		Nov 21, 2003	Code re-write. Conversion to pointer based 
											strategy.
******************************************************************************/
void 
CheckCAN_Activity(void)
{
													/* is it no longer in sleep mode due to CAN bus activity */
	if (CAN_Ptr->canctl1.bit.slpak == 0)	
	{
													/* if CAN message not received */
		if (CAN_Ptr->canrflg.bit.rxf == 0)  
		{											/* then check if bus speed change is due */
			if (CAN_TIMEOUT_COUNTER < CAN_ACK_WINDOW)
			{										/* if not, update the timeout */
				CAN_TIMEOUT_COUNTER++;
			}
			else 	 
			{										/* if time out has expired then try a different CAN bus speed */
													/* select next CAN bus speed (zeros the CAN_TIMEOUT_COUNTER)  */ 
				CAN_BusSpeed();   
			}						
		}											/* has the MCU synchronized with the CAN bus */
		else	  
		{
				 									/* if address pointer message received */
			if (CAN_Ptr->rxbuf.id.w[0] == CAN_ADDRESS_POINTER_ID)
			{										/* receive CAN download */
				CAN_Rx();				  
			}
			else
			{						         	/* clear receive buffer full flag and carry on with the synchronisation loop */
				CAN_Ptr->canrflg.bit.rxf = 1;		
			}
		}				
	}
													/* carry on around the main loop */
}

/******************************************************************************
Function Name	:	CAN_Rx
Engineer			:	Joanne McNamee 
Date				:	17 dec 2002

Parameters		:	none
Returns			:	none
Globals			:  Requires *CAN_Ptr be initialized

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.
						
						Checking that the data pointer is initialized, in the data 
						download loop, is not strictly necessary as this routine is
						only called if a data pointer message has been received -
						the routine is called with the address pointer value still in 
						the CAN Rx buffer and with the RXF flag still set so it falls
						through the first time and initializes the data pointer and 
						checksum.			:	

Modified:
Engineer				Date				Reason
Joanne McNamee		Aug 31, 2003	New project modifications to existing 
											software.
Martyn Gallop		Nov 21, 2003   Code re-write. Conversion to pointer based 
											strategy.
Martyn Gallop		Nov 26, 2003	Change to using local pointers for  
											download loop count and address pointer to
											reduce loop time.
											Change from case to if/elseif structure to
											reduce loop time.
Martyn Gallop		Nov 26, 2003	Changed most variables from global to local.
Grant More			Dec 01, 2003	Added check for initialized address pointer 
											before data can be downloaded.
Daniel Mckenna    Oct 04, 2006   Cleared rxf flag before executing received program 
                                 to allow compatibility with a secondary bootloader.
                                 Added SCI_or_CAN variable to denote to secondary 
                                 bootloader which interface is in use. 
******************************************************************************/
void 
CAN_Rx(void)
{
	tU08		txSelect;						/* used when selecting transmit buffer  */

	tU08		checkSumTotal;					/* 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 */
  	tU08 		*addressPtr = 0;				/* used to control where each downloaded data byte is stored */
  	tS08 		loopCounter;					/* used to count the number of bytes to download from a code data message */

	CanFlags.bit.SCI_or_CAN = 1;   /* denotes to secondary bootloader that CAN interface is in use */

	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)			
		{											/* down load data only if addressPtr is initialized */
		   if (addressPtr != (tU08*) 0)
		   {										/* download loop implemented as a down count because test for zero is more efficient */
      		for (loopCounter = (CAN_Ptr->rxbuf.dlr.byte)-1; loopCounter >=0; loopCounter--)
      		{
      			*(addressPtr+loopCounter) = CAN_Ptr->rxbuf.dsr[loopCounter];
      			checkSumTotal += *(addressPtr+loopCounter);			
      		} 
      		addressPtr += CAN_Ptr->rxbuf.dlr.byte;
      	}
      	else							   	/* address pointer not initialized - ignore data */
      	{}
   	} 		
													/* 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 = *(tU08 **)CAN_Ptr->rxbuf.dsr;	
													/* checksum value is always re-initialized on receipt of */
													/* an address pointer. Allows multiple instances of data download  */
			checkSumTotal = CAN_Ptr->rxbuf.dsr[0]; 
			checkSumTotal += CAN_Ptr->rxbuf.dsr[1] ;
      }
													/* 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 */
			if (receivedCheckSum == checkSumTotal)
			{
				CAN_Ptr->txbuf.dsr[0] = CHECKSUM_OK;
			}
			else
			{
				CAN_Ptr->txbuf.dsr[0] = CHECKSUM_BAD;
			}
			
			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)
		{
													/* set address pointer where to execute code from */
			addressPtr = *(tU08 **)CAN_Ptr->rxbuf.dsr;		
									         	/* clear receive buffer full flag */
	   	CAN_Ptr->canrflg.bit.rxf = 1;		
													    /****************************************/
			asm ldx	addressPtr;			/******* LRAE PASSES EXECUTION TO *******/
			asm jmp	0,x;		 				/******** DOWNLOADED CODE IN RAM ********/
		}													/****************************************/
									         	/* clear receive buffer full flag */
		CAN_Ptr->canrflg.bit.rxf = 1;		
	}
}

#endif /* CAN0_PRESENT */
										