/**##########################################################################**/
/**     Author    : Matthew Grant                                            **/
/**     Device    : MC9S12XEP100                                             **/
/**     DataSheet : MC9S12XEP100 Reference Manual v1.10                      **/
/**     Compiler  : Freescale Code Warrior C compiler for S12X v4.6          **/
/**     Date/Time : 10.23.2007                                               **/
/**     Company   : Freescale Semiconductor Inc.                             **/
/**     Purpose   : This project intends to provide a quick, easy to         **/
/**                 understand example of the emulated EEPROM(EEE)           **/
/**                 capability of the enhanced Flash Technology Module (FTM) **/
/**                 present in the S12XE family of microcontrollers. Although**/
/**                 there are a variety of details associated with the use   **/
/**                 of EEE, the focus of this example is with the            **/
/**                 partitioning, reading, and writing of EEEPROM data.      **/
/**                 This project is also associated with application note    **/
/**                 AN3743 (EEEPROM Quick Start) and has steps that are      **/
/**                 referenced in the document.                              **/
/**     Setup     : This project requires an S12XEP100 microcontroller on    **/
/**                 a board or device that allows the microcontroller to     **/
/**                 become functional. Only internal resources are used,     **/
/**                 and no direct manipulation of I/O takes place. Several   **/
/**                 target communication devices should be selectable with   **/
/**                 this example project. Remember to visit the CLOCK DIVIDE **/
/**                 section and select the oscillator setting matching your  **/
/**                 board.                                                   **/
/**                                                                          **/
/**     Note      :                                                          **/
/**##########################################################################**/
#include <hidef.h>                         //Common defines and macros
#include <MC9S12XEP100.h>                  //Derivative information
#pragma LINK_INFO DERIVATIVE "mc9s12xep100"

#include <string.h>
#include "xgate.h"

typedef struct    //Used to copy and store Flash error status registers.
{
  unsigned char fstat_var;
  unsigned char ferfstat_var;
}ErrType;

/************************* #DEFINES ******************************************/
/**** P-Flash and D-Flash Commands ****/

#define ERASE_VERIFY_ALL_BLOCKS  0x01 
// Verify that all program and data Flash blocks are erased.
// CCOBIX end = 0
// CCOB Params - NONE
// MGSTAT set if fault

#define ERASE_VERIFY_BLOCK       0X02      
// Verify that a Flash block is erased.
// CCOBIX end = 0
// CCOB Params - gpage
// MGSTAT set if fault

#define ERASE_ALL_BLOCKS         0x08 
// Erase all program and data Flash blocks.
// An erase of all Flash blocks is only possible when the FPLDIS, FPHDIS, and FPOPEN
// bits in the FPROT register and the EPDIS and EPOPEN bits in the EPROM register are
// set prior to launching the command.
// CCOBIX end = 0
// CCOB Params - NONE
// MGSTAT set if fault, FPVIOL / ACCERR set where appropriate

#define UNSECURE_FLASH           0x0B 
// Supports a method of releasing MCU security by erasing all program and data Flash
// blocks and verifying that all program and data Flash blocks are erased.
// CCOBIX end = 0
// CCOB Params - NONE
// MGSTAT set if fault

#define SET_USER_MARGIN_LEVEL    0x0D 
// Specifies a user margin read level for all program Flash blocks.
// CCOBIX end = 1
// CCOB Params - gpage, level setting (0-2) in CCOB[1]
// ACCERR set if invalid level

#define SET_FIELD_MARGIN_LEVEL   0x0E 
// Specifies a field margin read level for all program Flash blocks (special modes only).
// CCOBIX end = 1
// CCOB Params - gpage, level setting (0-4) in CCOB[1]
// ACCERR set if invalid level

/* **** P-Flash Only Commands ****/

#define ERASE_VERIFY_P_FLASH_SECTION 0x03  
// Verify that a given number of words starting at the address provided are erased.
// CCOBIX end = 2
// CCOB Params - global address, number of phrases in CCOB[2]
// MGSTAT set if fault

#define READ_ONCE	               0x04  
// Read a phrase from a dedicated 64 word area in a hidden region of a programFlash block
// that was previously programmed using the Program Once command.
// CCOBIX end = 1
// CCOB Params - read once index (0-3) in CCOB[1], phrase in CCOB [5:2]
// returns phrase in CCOB [4:1]

#define LOAD_DATA_FIELD          0x05 
// Load data for simultaneous multiple program Flash block operations.
// CCOBIX end = 5
// CCOB Params - global address, phrase in CCOB [5:2]

#define PROGRAM_P_FLASH          0x06 
// Program a phrase in a program Flash block and any previously loaded phrases for any
// other program Flash block (see Load Data Field command).
// CCOBIX end = 5
// CCOB Params - global address, phrase in CCOB [5:2]
// MGSTAT set if fault, FPVIOL / ACCERR set where appropriate

#define PROGRAM_ONCE             0x07 
// Program a dedicated 64 word area in a hidden region of a program Flash block that is
// allowed to be programmed only once.
// CCOBIX end = 5
// CCOB Params - read once index (0-3) in CCOB[1], phrase in CCOB [5:2]
// MGSTAT set if fault

#define ERASE_P_FLASH_BLOCK      0x09 
// Erase a program Flash block.
// An erase of the full program Flash block is only possible when FPLDIS, FPHDIS and
// FPOPEN bits in the FPROT register are set prior to launching the command.
// CCOBIX end = 1
// CCOB Params - global address
// MGSTAT set if fault, FPVIOL / ACCERR set where appropriate

#define ERASE_P_FLASH_SECTOR 0x0A 
// Erase all bytes in a program Flash sector.
// CCOBIX end = 1
// CCOB Params - global address
// MGSTAT set if fault, FPVIOL / ACCERR set where appropriate

#define VERIFY_BACKDOOR_ACCESS_KEY 0x0C 
// Supports a method of releasing MCU security by verifying a set of security keys.
// CCOBIX end = 4
// CCOB Params - backdoor key in CCOB [1:4]
// ACCERR set if not verified

/**** D-Flash Only Commands ****/

#define ENABLE_D_FLASH 0x0F 
// Partition a section of D-Flash for user access and EEE.
// CCOBIX end = 2
// CCOB Params - number of sectors for D-Flash in CCOB[1],  number of sectors for EEE in CCOB[2]
// ACCERR set if fault

#define FULL_PARTITION_D_FLASH 0x0F 
// Partition a section of D-Flash for user access and EEE.
// CCOBIX end = 2
// CCOB Params - number of sectors for D-Flash in CCOB[1],  number of sectors for EEE in CCOB[2]
// ACCERR set if fault

#define ERASE_VERIFY_D_FLASH_SECTION 0x10 
// Verify that a given number of words starting at the address provided are erased.
// CCOBIX end = 2
// CCOB Params - global address of first word, number of words to verify CCOB[2]
// MGSTAT set if fault

#define PROGRAM_D_FLASH         0x11 
// Program up to four words in the data Flash block (see Load Data Field command).
// CCOBIX end = 2
// CCOB Params - global address, up to 4 data words in CCOB [2:5]
// MGSTAT set if fault, EPVIOL / ACCERR set where appropriate

#define ERASE_D_FLASH_SECTOR    0x12 
// Erase all bytes in a data Flash sector.
// CCOBIX end = 2
// CCOB Params - global address
// MGSTAT set if fault, EPVIOL  set where appropriate

#define ENABLE_EEPROM_EMULATION    0x13 
// Requests the FTMSM to enable EEPROM emulation.
// CCOBIX end = 0
// CCOB Params - NONE

#define DISABLE_EEPROM_EMULATION   0x14 
// Requests the FTMSM to suspend all current erase and program activity related to
// EEPROM emulation but leave current EEE tags set.
// CCOBIX end = 0
// CCOB Params - NONE

#define CANCEL_EEPROM_EMULATION    0x15   /* M22E mask only */
// Requests the FTMSM to suspend all current erase and program activity related to
// EEPROM emulation and clear all outstanding EEE tags.
// CCOBIX end = 0
// CCOB Params - NONE

#define EEPROM_QUERY    0x15   /* M48H mask only */
// Requests EEE status information.
// CCOBIX end = 0
// CCOB Return Params -
// CCOB[1] DFPART - size of D-Flash user partition (x256 bytes)
// CCOB[2] ERPART - size of EEE ram (x256 bytes)
// CCOB[3] ECOUNT - typical number of erase cycles for the EEE sectors
// CCOB[4] Dead sector count / Ready sector count

#define PARTITION_D_FLASH 0x20  /* M48H mask only */
// Partition a section of D-Flash for user access and EEE.
// CCOBIX end = 2
// CCOB Params - number of sectors for D-Flash in CCOB[1],  number of sectors for EEE in CCOB[2]
// ACCERR set if fault

//CLOCK DIVIDE SECTION
//Uncomment the FCLK_DIV value according to the oscillator crystal.
//These values are chosen from the MC9S12XE100 datasheet.
//#define FCLK_DIV 0x1    // Flash clock divider for 2MHz crystal
#define FCLK_DIV 0x3    // Flash clock divider for 4MHz crystal
//#define FCLK_DIV 0x7    // Flash clock divider for 8MHz crystal
//#define FCLK_DIV 0x9    // Flash clock divider for 10MHz crystal
//#define FCLK_DIV 0x18   // Flash clock divider for 26MHz crystal

#define FDIV_NOT_SET       0x01
#define COMMAND_BUSY       0x02
#define PARTITION_MISMATCH 0x04
#define CCIF_MASK          0x80

// bit masks for FSTAT
#define mgstat0 0x01  
#define mgstat1 0x02
#define mgbusy  0x08
#define fpviol  0x10
#define accerr  0x20
#define ccif    0x80

//bit masks for FERSTAT
#define sfdif    0x01
#define dfdif    0x02
#define ersvif0  0x04
#define ersvif1  0x08
#define epviolif 0x10
#define pgmerif  0x40
#define erserif  0x80



//EEE SIZE SECTION
/**** SET THE SIZE OF THE EEE HERE ****/
#define EEE_RAM 1 //Specify the # of EEE RAM sections for the FTM (Flash
                  //module) to use as EEE. The valid range is 0-16 sections.

/**** CALCULATES AMOUNT OF DATA FLASH FOR GENERAL USE ****/
#if(EEE_RAM == 1)
   #define DFPART (128 - 12)
#else
   #define DFPART (128 - (8 * EEE_RAM))
#endif


/******************************************************************************/
void ErrorCheck(volatile ErrType status, volatile unsigned char statbits,
            volatile unsigned char ferstatbits)					
{
  //Compare the copied FSTAT and FERSTAT register results against the selected
  //error bits. A match indicates an error and loops forever.
  if((status.fstat_var & statbits) || (status.ferfstat_var & ferstatbits))
  {
    while(1); //Wait forever.
  }
}
/******************************************************************************/
void ReportError(unsigned char error_code)					
{
  //User reported errors are sent here.
  while(1);
}
/******************************************************************************
Function  : LaunchFlashCommand
Engineer  :	Matt Grant
Date      : 02/18/2008
Arguments : The following parameters are used as specified in the command list of the Flash block guide.
          :	params: Indicates # of word parameters used from ccob1 - ccob7, if any.
          : ccob0high: Indicates the high byte command portion of the ccob0 word.
          : ccob0low: Forms the low byte of the ccob0 word parameter, if needed.
          : ccob1: First word parameter, if needed.
          : ccob2: Second word parameter, if needed.
          : ccob3: Third word parameter, if needed.
          : ccob4: Fourth word parameter, if needed.
          : ccob5: Fifth word parameter, if needed.
          : ccob6: Sixth word parameter, if needed.
          : ccob7: Seventh word parameter, if needed.
Return    : Flash module status registers
Notes     : Clears any error flags and uses parameters to initiate an FTM command.
            Does not check if the Flash is erased, and does not explicitely
            verify that data has been sucessfully programmed.
******************************************************************************/
//unsigned char LaunchFlashCommand(char params, unsigned char command, unsigned char ccob0,
ErrType LaunchFlashCommand(char params, unsigned char ccob0high, unsigned char ccob0low, unsigned int ccob1,
unsigned int ccob2,unsigned int ccob3,unsigned int ccob4,unsigned int ccob5, unsigned int ccob6, unsigned int ccob7)
{
unsigned char temp;                 //Temporary variable.
volatile ErrType status;            //Used to copy and store Flash status registers.

  temp = FCLKDIV;                   //Read the FCLKDIV register
  if((temp & 0x80) != 0x80)         //If FDIVLD not set, then write FCLKDIV.
  {
    FCLKDIV = FCLK_DIV;             //Write FCLKDIV before launching FTM command.
    if(FCLKDIV != (FCLK_DIV | 0x80))//Check to make sure value is written.
      ReportError(0);
  }
  while(FSTAT_CCIF != 1);           //Wait a while for CCIF to be set
  if(FSTAT_CCIF == 1)               //Ensure CCIF set before using the FTM module.
	{																	
	  FSTAT = 0x30;                   //Use store instruction to clear ACCERR, FPVIOL.
    /**********SET UP THE FTM COMMAND AND PARAMETERS***********/
    FCCOBIX = 0;                    //Set CCOB index to 0 to begin command setup.
    FCCOBHI = ccob0high;            //Write ccob0 high-byte command value.
    FCCOBLO = ccob0low;             //Write ccob0 low-byte parameter, if used.
    if (params > 0)                 //Continue if more parameters to specify.
    {
      FCCOBIX++;
      FCCOB = ccob1;                //Write next word parameter to CCOB1 buffer.
      if (params > 1)               //Continue if more parameters to specify.
      {
        FCCOBIX++;
        FCCOB = ccob2;              //Write next word parameter to CCOB2 buffer.
        if (params > 2)             //Continue if more parameters to specify.
        {
          FCCOBIX++;
          FCCOB = ccob3;            //Write next word parameter to CCOB3 buffer.
          if (params > 3)           //Continue if more parameters to specify.
          {
            FCCOBIX++;
            FCCOB = ccob4;          //Write next word parameter to CCOB4 buffer.
            if (params > 4)         //Continue if more parameters to specify.
            {
              FCCOBIX++;
              FCCOB = ccob5;        //Write next word parameter to CCOB5 buffer.
              if (params > 5)       //Continue if more parameters to specify.
              {
                FCCOBIX++;
                FCCOB = ccob6;      //Write next word parameter to CCOB6 buffer.
                if (params > 6)     //Continue if more parameters to specify.
                {
                  FCCOBIX++;
                  FCCOB = ccob7;    //Write next word parameter to CCOB7 buffer.
                }
              }
            }
          }
        }
      }
    }
    /**********************************************************/
    FSTAT = 0x80;                   //Clear buffer-empty-flag to start FTM command.
    while (!FSTAT_CCIF);            //Now wait for the FTM command to complete.
    status.fstat_var = FSTAT;       //Copy FSTAT register state for later comparison.
    status.ferfstat_var = FERSTAT;  //Copy FERSTAT register state for later comparison.
    return(status);                 //After FTM command completed, return status.
	} 
	else 
    ReportError(COMMAND_BUSY);      //FTM is currently busy.
}
/******************************************************************************/
char * copy_string(char * source, char * dest)
{
char index = 0;

  while((source[index] != 0) && (index < 16))
  {
    dest[index] = source[index];    //Copy source char to destination location.
    index++;                        //Incement index to next character position.
  }
  return(&dest[--index]);           //Return last character address.
}
/******************************************************************************/
void main(void)
{
volatile char before[]="BEFORE"; //Chars to be written to the EEE and Buffer RAM
volatile char during[]=" DURING";//Chars to be written to the EEE and Buffer RAM
volatile char after[]=" AFTER";  //Chars to be written to the EEE and Buffer RAM
signed char dfPart, erPart;      //Holds EEE partition size of data Flash and buffer RAM
char * section1_ptr;             //Points to location within section1 of buffer RAM
char * section2_ptr;             //Points to location within section2 of buffer RAM
volatile ErrType status;


  EnableInterrupts;
  section1_ptr = (signed char *) 0x0F00;//Assign beginning address of section1 of
                                        //EEE RAM to pointer.
  section2_ptr = (signed char *) 0x0E00;//Assign beginning address of section2 of
                                        //buffer RAM to pointer.
  
  /*********(1)SET UP THE FLASH CLOCK DIVIDE REGISTER********/
  while (!FSTAT_CCIF)               //Wait for FTM to be ready
  {
  }
  FCLKDIV = FCLK_DIV;               //Now write FCLKDIV before using the FTM.
  if(FCLKDIV != (FCLK_DIV | 0x80))  //Ensure the value is written.
    ReportError(FDIV_NOT_SET);      //Report an error code.
  /**********************************************************/

  /****(2)CHECK BUFFER RAM CONTENTS FROM THE PREVIOUS RUN****/
  //Before any FTM commands are executed, check the debugger memory
  //window and observe the contents of sections 1 (0xF00 to 0xFFF)
  //and 2 (0xE00 to 0xEFF) of the buffer RAM.
  /**********************************************************/
     
  //This can ONLY be used in DEBUG mode!
  #define FORCE_PARTITON_FOR_DEBUG  //Allows the code to partition specified sections
                                    //of DFlash and buffer RAM for use as emulated EEPROM.
                                    //This command also erases the sections partitioned for EEPROM.
  #define ENABLE_EEE_RECORD_WRITING //Allows FTM to create records in DFlash to manage
                                    //the values written in allocated sectors of buffer
                                    //RAM. If EEE record writing is disabled, then data
                                    //written to the buffer RAM, even if partitioned for
                                    //use as emulated EEPROM, will not be saved after
                                    //a power cycle.

/***********(3)PERFORM FULL PARTITION OF EEE RAM***********/
#ifdef FORCE_PARTITON_FOR_DEBUG     //If defined, force EEE partition and eraser.

  //Run LaunchFlashCommand to partition and erase the EEE in debug mode.
  //Return the status of the FSTAT and FERSTAT Flash registers.
  status = LaunchFlashCommand(2 ,FULL_PARTITION_D_FLASH, 0, DFPART, EEE_RAM, 0, 0, 0, 0, 0);
  
  //Always check if any error bits are set.
  ErrorCheck(status, accerr|fpviol|mgstat1|mgstat0, erserif|pgmerif|epviolif|ersvif1|ersvif0|dfdif|sfdif);
#endif   
/**********************************************************/

  /*****************(4)CHECK EEE PARTITIONING****************/
  //Use flash command function to query the EEEPROM partitioning.
  //Return the status of the FSTAT and FERSTAT Flash registers.
  status = LaunchFlashCommand(0 ,EEPROM_QUERY, 0, 0, 0, 0, 0, 0, 0, 0); //Check the EEE status  

  //Check if any error bits are set.
  ErrorCheck(status, (accerr|fpviol|mgstat1|mgstat0), (erserif|pgmerif|epviolif|ersvif1|ersvif0|dfdif|sfdif));

  FCCOBIX = 1;      //Set CCOB index to get Data flash partition result.
  dfPart = FCCOBLO; //Copy Data flash partition result.
  FCCOBIX++;        //Set CCOB index to get EEE RAM partition result.
  erPart = FCCOBLO; //Copy EEE RAM partition result.

#ifdef FORCE_PARTITON_FOR_DEBUG //If defined, check that the FTM properly allocated
                                //and erased the EEE sectors in buffer RAM.

  //Use query results to determine if D-Flash has been partitioned
  //for EEEPROM with erPart == EEE_RAM and dfPart == DFPART.
  if (EEE_RAM > 0)
  {
    if((erPart != EEE_RAM) || (dfPart != DFPART))
      ReportError(PARTITION_MISMATCH);     //Full Partition was UNsuccessful
  }
  else if((erPart != -1) || (dfPart != -1))
  {
    ReportError(PARTITION_MISMATCH);       //EEE_RAM might be out of range (0-16)
  }
#endif
  /**********************************************************/

  /****(5)WRITE PATTERN TO EEE SECTIONS BEFORE EEE ENABLED***/
  //Now write the character pattern, "BEFORE", to the beginning of the
  //1st and 2nd buffer RAM sections. The 1st 256byte section is partitioned
  //as EEE RAM. The 2nd and remaining sections are general user RAM.
  section1_ptr = copy_string(&before, section1_ptr);//Copy "BEFORE" to EEE RAM
  section2_ptr = copy_string(&before, section2_ptr);//Copy "BEFORE" to user RAM
  /**********************************************************/

/**************(6)ENABLE WRITES OF EEE RECORDS***************/
//This enables the FTM to take any revised data written to the EEE
//partitioned section(s) of the buffer RAM and update the record(s)
//in the EEE partitioned section(s) of data Flash.
#ifdef ENABLE_EEE_RECORD_WRITING
  if(erPart > 0)
  {
    //Use flash command function to enable the FTM to manage EEE data and records.
    //Return the status of the FSTAT and FERSTAT Flash registers.
    status = LaunchFlashCommand(0 ,ENABLE_EEPROM_EMULATION, 0, 0, 0, 0, 0, 0, 0, 0);

    //Check if any error bits are set.
    ErrorCheck(status, (accerr|fpviol|mgstat1|mgstat0), (erserif|pgmerif|epviolif|ersvif1|ersvif0|dfdif|sfdif));
  }
/**********************************************************/

  /****(7)WRITE PATTERN TO EEE SECTORS WHILE EEE ENABLE****/
  //Now write the character pattern, "DURING", after the character string,
  //"BEFORE", which is at the beginning of the 1st and 2nd buffer RAM sections.
  //The 1st 256byte section is partitioned as EEE RAM. The 2nd and remaining
  //sections are general user RAM.
  section1_ptr++;                                   //Move to next character position
  section2_ptr++;                                   //Move to next character position
  section1_ptr = copy_string(&during, section1_ptr);//Copy "DURING" to EEE RAM
  section2_ptr = copy_string(&during, section2_ptr);//Copy "DURING" to user RAM
  /**********************************************************/
    
  /**************(8)DISABLE WRITES OF EEE RECORDS**************/
  if(erPart > 0)
  {
    //Wait for FTM to store new EEE RAM data into EEE Flash.
    while(FSTAT_MGBUSY || ETAG)
    {
      //Wait for FTM to finish writing data records to EEE Flash.
    }
  
    //Use to DISable the FTM from managing EEE data and records.
    //Return the status of the FSTAT and FERSTAT Flash registers.
    status = LaunchFlashCommand(0 ,DISABLE_EEPROM_EMULATION, 0, 0, 0, 0, 0, 0, 0, 0);

    //Check if any error bits are set.
    ErrorCheck(status, (accerr|fpviol|mgstat1|mgstat0), (erserif|pgmerif|epviolif|ersvif1|ersvif0|dfdif|sfdif));
  }
#endif
/************************************************************/
  
  /*****(9)WRITE PATTERN TO EEE SECTORS AFTER EEE DISABLE****/
  //Now write the character pattern, "AFTER", after the character string,
  //"DURING", which is at the beginning of the 1st and 2nd buffer RAM sections.
  //The 1st 256byte section is partitioned as EEE RAM. The 2nd and remaining
  //sections are general user RAM.
  section1_ptr++;                                  //Move to next character position
  section2_ptr++;                                  //Move to next character position
  section1_ptr = copy_string(&after, section1_ptr);//Copy "AFTER" to EEE RAM
  section2_ptr = copy_string(&after, section2_ptr);//Copy "AFTER" to user RAM
  /**********************************************************/

  for(;;) {} //Wait forever
  /* please make sure that you never leave this function */

  /**************(10)NOW RESET, THEN POWER CYCLE*************/
  //   USE THE DEBUGGER MEMORY WINDOW TO OBSERVE THE STRING PATTERNS
  //   WRITTEN TO THE EEE RAM AND USER RAM.
  //A) PRESS RESET IN THE DEBUGGER AND NOTICE THE TEXT,"AFTER"
  //   DISSAPPEARS FROM THE MEMORY WINDOW, DEMONSTRATING WHAT
  //   HAPPENS TO DATA WHEN IT HAS BEEN WRITTEN TO EEE RAM WITHOUT A
  //   CORRESPONDING RECORD BEING CREATED IN EEE FLASH.
  //B) NOW CLOSE THE DEBUGGER AND POWER CYCLE THE MCU TO ALLOW THE
  //   BUFFER RAM AND EEE RAM TO LOSE ALL OF ITS CONTENTS. THEN USE
  //   THE DEBUGGER TO RECONNECT TO THE MCU. THIS MAY ALSO BE DONE
  //   BY REPROGRAMING THE SAME CODE INTO THE MCU.
  //C) WITHOUT RUNNING THE CODE, USE THE MEMORY WINDOW IN THE DEBUGGER
  //   TO OBSERVE THE CONTENTS OF THE EEE RAM. NOTICE THE "BEFORE" AND
  //   "DURING" STRINGS HAVE AUTOMATICALLY BEEN COPIED BACK INTO THE
  //   EEE RAM BY THE FTM AFTER RESET.
  /**********************************************************/
}
