An I/O controller for virtual pinball machines: accelerometer nudge sensing, analog plunger input, button input encoding, LedWiz compatible output controls, and more.

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers FreescaleIAP.cpp Source File

FreescaleIAP.cpp

00001 // FreescaleIAP - custom version
00002 //
00003 // This is a simplified version of Erik Olieman's FreescaleIAP, a flash 
00004 // memory writer for Freescale boards.  This version combines erase, write,
00005 // and verify into a single API call.  The caller only has to give us a
00006 // buffer (of any length) to write, and the address to write it to, and
00007 // we'll do the whole thing - essentially a memcpy() to flash.
00008 //
00009 // This version uses an assembler implementation of the core code that
00010 // launches an FTFA command and waits for completion, to minimize the
00011 // size of the code and to ensure that it's placed in RAM.  The KL25Z
00012 // flash controller prohibits any flash reads while an FTFA command is
00013 // executing.  This includes instruction fetches; any instruction fetch
00014 // from flash while an FTFA command is running will fail, which will 
00015 // freeze the CPU.  Placing the execute/wait code in RAM ensures that
00016 // the wait loop itself won't trigger a fetch.  It's also vital to disable
00017 // interrupts while the execute/wait code is running, to ensure that we
00018 // don't jump to an ISR in flash during the wait.
00019 //
00020 // Despite the dire warnings in the hardware reference manual about putting
00021 // the FTFA execute/wait code in RAM, it doesn't actually appear to be
00022 // necessary, as long as the wait loop is very small (in terms of machine
00023 // code instruction count).  In testing, Erik has found that a flash-resident
00024 // version of the code is stable, and further found (by testing combinations
00025 // of cache control settings via the platform control register, MCM_PLACR)
00026 // that the stability comes from the loop fitting into CPU cache, which
00027 // allows the loop to execute without any fetches taking place.  Even so,
00028 // I'm keeping the RAM version, out of an abundance of caution: just in
00029 // case there are any rare or oddball conditions (interrupt timing, say) 
00030 // where the cache trick breaks.  Putting the code in RAM seems pretty
00031 // much guaranteed to work, whereas the cache trick seems somewhat to be
00032 // relying on a happy accident, and I personally don't know the M0+ 
00033 // architecture well enough to be able to convince myself that it really
00034 // will work under all conditions.  There doesn't seem to be any benefit
00035 // to not using the assembler, either, as it's very simple code and takes
00036 // up little RAM (about 40 bytes).
00037 
00038 
00039 #include "FreescaleIAP.h"
00040 
00041 //#define IAPDEBUG
00042 
00043 // assembly interface
00044 extern "C" {
00045     // Execute the current FTFA command and wait for completion.
00046     // This is an assembler implementation that runs entirely in RAM,
00047     // to ensure strict compliance with the prohibition on reading
00048     // flash (for instruction fetches or any other reason) during FTFA 
00049     // execution.
00050     void iapExecAndWait();
00051 }
00052 
00053 enum FCMD {
00054     Read1s = 0x01,
00055     ProgramCheck = 0x02,
00056     ReadResource = 0x03,
00057     ProgramLongword = 0x06,
00058     EraseSector = 0x09,
00059     Read1sBlock = 0x40,
00060     ReadOnce = 0x41,
00061     ProgramOnce = 0x43,
00062     EraseAll = 0x44,
00063     VerifyBackdoor = 0x45
00064 };
00065 
00066 // Get the size of the flash memory on the device
00067 uint32_t FreescaleIAP::flashSize(void) 
00068 {
00069     uint32_t retval = (SIM->FCFG2 & 0x7F000000u) >> (24-13);
00070     if (SIM->FCFG2 & (1<<23))           // Possible second flash bank
00071         retval += (SIM->FCFG2 & 0x007F0000u) >> (16-13);
00072     return retval;
00073 }
00074 
00075 // Check if an error occurred
00076 static FreescaleIAP::IAPCode checkError(void) 
00077 {
00078     if (FTFA->FSTAT & FTFA_FSTAT_FPVIOL_MASK) {
00079         #ifdef IAPDEBUG
00080         printf("IAP: Protection violation\r\n");
00081         #endif
00082         return FreescaleIAP::ProtectionError;
00083     }
00084     if (FTFA->FSTAT & FTFA_FSTAT_ACCERR_MASK) {
00085         #ifdef IAPDEBUG
00086         printf("IAP: Flash access error\r\n");
00087         #endif
00088         return FreescaleIAP::AccessError;
00089     }
00090     if (FTFA->FSTAT & FTFA_FSTAT_RDCOLERR_MASK) {
00091         #ifdef IAPDEBUG
00092         printf("IAP: Collision error\r\n");
00093         #endif
00094         return FreescaleIAP::CollisionError;
00095     }
00096     if (FTFA->FSTAT & FTFA_FSTAT_MGSTAT0_MASK) {
00097         #ifdef IAPDEBUG
00098         printf("IAP: Runtime error\r\n");
00099         #endif
00100         return FreescaleIAP::RuntimeError;
00101     }
00102     return FreescaleIAP::Success;
00103 }
00104 
00105 // check for proper address alignment
00106 static bool checkAlign(int address) 
00107 {
00108     bool retval = address & 0x03;
00109     #ifdef IAPDEBUG
00110     if (retval)
00111         printf("IAP: Alignment violation\r\n");
00112     #endif
00113     return retval;
00114 }
00115 
00116 // clear errors in the FTFA
00117 static void clearErrors()
00118 {
00119     // wait for any previous command to complete    
00120     while (!(FTFA->FSTAT & FTFA_FSTAT_CCIF_MASK)) ;
00121 
00122     // clear the error bits
00123     if (FTFA->FSTAT & (FTFA_FSTAT_ACCERR_MASK | FTFA_FSTAT_FPVIOL_MASK))
00124         FTFA->FSTAT |= FTFA_FSTAT_ACCERR_MASK | FTFA_FSTAT_FPVIOL_MASK;
00125 }
00126 
00127 static FreescaleIAP::IAPCode eraseSector(int address) 
00128 {
00129     #ifdef IAPDEBUG
00130     printf("IAP: Erasing sector at %x\r\n", address);
00131     #endif
00132 
00133     // ensure proper alignment
00134     if (checkAlign(address))
00135         return FreescaleIAP::AlignError;
00136     
00137     // clear errors
00138     clearErrors();
00139     
00140     // Set up the command
00141     FTFA->FCCOB0 = EraseSector;
00142     FTFA->FCCOB1 = (address >> 16) & 0xFF;
00143     FTFA->FCCOB2 = (address >> 8) & 0xFF;
00144     FTFA->FCCOB3 = address & 0xFF;
00145     
00146     // execute
00147     iapExecAndWait();
00148     
00149     // check the result
00150     return checkError();
00151 }
00152 
00153 static FreescaleIAP::IAPCode verifySectorErased(int address)
00154 {
00155     // Always verify in whole sectors.  The
00156     const unsigned int count = SECTOR_SIZE/4;
00157 
00158     #ifdef IAPDEBUG
00159     printf("IAP: Verify erased at %x, %d longwords (%d bytes)\r\n", address, count, count*4);
00160     #endif
00161     
00162     if (checkAlign(address))
00163         return FreescaleIAP::AlignError;
00164 
00165     // clear errors
00166     clearErrors();
00167     
00168     // Set up command
00169     FTFA->FCCOB0 = Read1s;
00170     FTFA->FCCOB1 = (address >> 16) & 0xFF;
00171     FTFA->FCCOB2 = (address >> 8) & 0xFF;
00172     FTFA->FCCOB3 = address & 0xFF;
00173     FTFA->FCCOB4 = (count >> 8) & 0xFF;
00174     FTFA->FCCOB5 = count & 0xFF;
00175     FTFA->FCCOB6 = 0;
00176 
00177     // execute    
00178     iapExecAndWait();
00179     
00180     // check the result
00181     FreescaleIAP::IAPCode retval = checkError();
00182     if (retval == FreescaleIAP::RuntimeError) {
00183         #ifdef IAPDEBUG
00184         printf("IAP: Flash was not erased\r\n");
00185         #endif
00186         return FreescaleIAP::EraseError;
00187     }
00188     return retval;       
00189 }
00190 
00191 // Write one sector.  This always writes a full sector, even if the
00192 // requested length is greater or less than the sector size:
00193 //
00194 // - if len > SECTOR_SIZE, we write the first SECTOR_SIZE bytes of the data
00195 //
00196 // - if len < SECTOR_SIZE, we write the data, then fill in the rest of the
00197 //   sector with 0xFF bytes ('1' bits)
00198 //
00199 
00200 static FreescaleIAP::IAPCode writeSector(int address, const uint8_t *p, int len)
00201 {    
00202     #ifdef IAPDEBUG
00203     printf("IAP: Writing sector at %x with length %d\r\n", address, len);
00204     #endif
00205 
00206     // program the sector, one longword (32 bits) at a time
00207     for (int ofs = 0 ; ofs < SECTOR_SIZE ; ofs += 4, address += 4, p += 4, len -= 4)
00208     {
00209         // clear errors
00210         clearErrors();
00211         
00212         // Set up the command
00213         FTFA->FCCOB0 = ProgramLongword;
00214         FTFA->FCCOB1 = (address >> 16) & 0xFF;
00215         FTFA->FCCOB2 = (address >> 8) & 0xFF;
00216         FTFA->FCCOB3 = address & 0xFF;
00217         
00218         // Load the longword to write.  If we're past the end of the source
00219         // data, write all '1' bits to the balance of the sector.
00220         FTFA->FCCOB4 = len > 3 ? p[3] : 0xFF;
00221         FTFA->FCCOB5 = len > 2 ? p[2] : 0xFF;
00222         FTFA->FCCOB6 = len > 1 ? p[1] : 0xFF;
00223         FTFA->FCCOB7 = len > 0 ? p[0] : 0xFF;
00224         
00225         // execute
00226         iapExecAndWait();
00227         
00228         // check errors
00229         FreescaleIAP::IAPCode status = checkError();
00230         if (status != FreescaleIAP::Success)
00231             return status;
00232     }
00233     
00234     // no problems
00235     return FreescaleIAP::Success;
00236 }
00237 
00238 // Program a block of memory into flash. 
00239 FreescaleIAP::IAPCode FreescaleIAP::programFlash(
00240     int address, const void *src, unsigned int length) 
00241 {    
00242     #ifdef IAPDEBUG
00243     printf("IAP: Programming flash at %x with length %d\r\n", address, length);
00244     #endif
00245     
00246     // presume success
00247     FreescaleIAP::IAPCode status = FreescaleIAP::Success;
00248     
00249     // Show diagnostic LED colors while writing.  I'm finally convinced this
00250     // is well and truly 100% reliable now, but I've been wrong before, so
00251     // we'll keep this for now.  The idea is that if we freeze up, we'll at
00252     // least know which stage we're at from the last color displayed.
00253     extern void diagLED(int,int,int);
00254     
00255     // try a few times if we fail to verify
00256     for (int tries = 0 ; tries < 5 ; ++tries)
00257     {
00258         // Do the write one sector at a time
00259         int curaddr = address;
00260         const uint8_t *p = (const uint8_t *)src;
00261         int rem = (int)length;
00262         for ( ; rem > 0 ; curaddr += SECTOR_SIZE, p += SECTOR_SIZE, rem -= SECTOR_SIZE)
00263         {
00264             // erase the sector (red LED)
00265             diagLED(1, 0, 0);
00266             if ((status = eraseSector(curaddr)) != FreescaleIAP::Success)
00267                 break;
00268             
00269             // verify that the sector is erased (yellow LED)
00270             diagLED(1, 1, 0);
00271             if ((status = verifySectorErased(curaddr)) != FreescaleIAP::Success)
00272                 break;
00273             
00274             // write the data (white LED)
00275             diagLED(1, 1, 1);
00276             if ((status = writeSector(curaddr, p, rem)) != FreescaleIAP::Success)
00277                 break;
00278                 
00279             // back from write (purple LED)
00280             diagLED(1, 0, 1);
00281         }
00282         
00283         // if we didn't encounter an FTFA error, verify the write
00284         if (status == FreescaleIAP::Success)
00285         {
00286             // Verify the write.  If it was successful, we're done.
00287             if (memcmp((void *)address, src, length) == 0)
00288             {
00289                 // LEDs to green on success
00290                 diagLED(0, 1, 0);
00291                 break;
00292             }
00293                 
00294             // We have a mismatch between the flash data and the source.
00295             // Flag the error and go back for another attempt.
00296             status = FreescaleIAP::VerifyError;
00297             diagLED(1, 0, 0);
00298         }
00299     }
00300     
00301     // return the result
00302     return status;
00303 }
00304