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
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
Generated on Wed Jul 13 2022 03:30:10 by 1.7.2