Mirror with some correction
Dependencies: mbed FastIO FastPWM USBDevice
FreescaleIAP/FreescaleIAP.cpp@76:7f5912b6340e, 2017-02-03 (annotated)
- Committer:
- mjr
- Date:
- Fri Feb 03 20:50:02 2017 +0000
- Revision:
- 76:7f5912b6340e
- Parent:
- 60:f38da020aa13
- Child:
- 77:0b96f6867312
Rework flash driver to make it truly stable (hopefully to 100% reliability); host-loaded configuration; performance improvements; more performance diagnostics.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
mjr | 76:7f5912b6340e | 1 | // FreescaleIAP, private version |
mjr | 76:7f5912b6340e | 2 | // |
mjr | 76:7f5912b6340e | 3 | // This is a heavily modified version of Erik Olieman's FreescaleIAP, a |
mjr | 76:7f5912b6340e | 4 | // flash memory writer for Freescale boards. This version is adapted to |
mjr | 76:7f5912b6340e | 5 | // the special needs of the KL25Z. |
mjr | 76:7f5912b6340e | 6 | // |
mjr | 76:7f5912b6340e | 7 | // Simplifications: |
mjr | 76:7f5912b6340e | 8 | // |
mjr | 76:7f5912b6340e | 9 | // Unlike EO's original version, this version combines erase and write |
mjr | 76:7f5912b6340e | 10 | // into a single opreation, so the caller can simply give us a buffer |
mjr | 76:7f5912b6340e | 11 | // and a location, and we'll write it, including the erase prep. We |
mjr | 76:7f5912b6340e | 12 | // don't need to be able to separate the operations, so the combined |
mjr | 76:7f5912b6340e | 13 | // interface is simpler at the API level and also lets us do all of the |
mjr | 76:7f5912b6340e | 14 | // interrupt masking in one place (see below). |
mjr | 76:7f5912b6340e | 15 | // |
mjr | 76:7f5912b6340e | 16 | // Stability improvements: |
mjr | 76:7f5912b6340e | 17 | // |
mjr | 76:7f5912b6340e | 18 | // The KL25Z has severe restrictions on flash writing that make it very |
mjr | 76:7f5912b6340e | 19 | // delicate. The key restriction is that the flash controller (FTFA) |
mjr | 76:7f5912b6340e | 20 | // doesn't allow any read operations while a sector erase is in progress. |
mjr | 76:7f5912b6340e | 21 | // The reason this complicates things is that all program code is stored |
mjr | 76:7f5912b6340e | 22 | // in flash by default. This means that every instruction fetch is a |
mjr | 76:7f5912b6340e | 23 | // flash read operation. The FTFA's response to a read while an erase is |
mjr | 76:7f5912b6340e | 24 | // in progress is to fail the read and return arbitrary data. When the |
mjr | 76:7f5912b6340e | 25 | // read is actually an instruction fetch, this results in the CPU trying |
mjr | 76:7f5912b6340e | 26 | // to execute random garbage, which virtually always crashes the program |
mjr | 76:7f5912b6340e | 27 | // and freezes the CPU. Making this even more complicated, the erase |
mjr | 76:7f5912b6340e | 28 | // operation runs a whole sector at a time, which takes a very long time |
mjr | 76:7f5912b6340e | 29 | // in CPU terms, on the order of milliseconds. Even if the code that |
mjr | 76:7f5912b6340e | 30 | // initiates the erase is very careful to loop without branching to any |
mjr | 76:7f5912b6340e | 31 | // flash locations, it's still a long time to go without an interrupt |
mjr | 76:7f5912b6340e | 32 | // occurring |
mjr | 76:7f5912b6340e | 33 | // |
mjr | 76:7f5912b6340e | 34 | // We use two strategies to avoid flash fetches while we're working. |
mjr | 76:7f5912b6340e | 35 | // First, the code that performs all of the FTFA operations is written |
mjr | 76:7f5912b6340e | 36 | // in assembly, in a module AREA marked READWRITE. This forces the |
mjr | 76:7f5912b6340e | 37 | // linker to put the code in RAM. The code could otherwise just have |
mjr | 76:7f5912b6340e | 38 | // well been written in C++, but as far as I know there's no way to tell |
mjr | 76:7f5912b6340e | 39 | // the mbed C++ compiler to put code in RAM. Since the FTFA code is all |
mjr | 76:7f5912b6340e | 40 | // in RAM, it doesn't trigger any flash fetches as it executes. Second, |
mjr | 76:7f5912b6340e | 41 | // we explicitly disable all of the peripheral interrupts that we use |
mjr | 76:7f5912b6340e | 42 | // anywhere in the program (USB, all the timers, etc) via the NVIC. It |
mjr | 76:7f5912b6340e | 43 | // isn't sufficient to disable interrupts with __disable_irq() or the |
mjr | 76:7f5912b6340e | 44 | // equivalent assembly instruction CPSID I; we have to turn them off at |
mjr | 76:7f5912b6340e | 45 | // the NVIC level. My understanding of ARM system architecture isn't |
mjr | 76:7f5912b6340e | 46 | // detailed enough to know why this is required, but experimentally it |
mjr | 76:7f5912b6340e | 47 | // definitely seems to be needed. |
mjr | 76:7f5912b6340e | 48 | |
mjr | 2:c174f9ee414a | 49 | #include "FreescaleIAP.h" |
mjr | 2:c174f9ee414a | 50 | |
mjr | 2:c174f9ee414a | 51 | //#define IAPDEBUG |
mjr | 76:7f5912b6340e | 52 | |
mjr | 76:7f5912b6340e | 53 | // assembly interface |
mjr | 76:7f5912b6340e | 54 | extern "C" { |
mjr | 76:7f5912b6340e | 55 | void iapEraseSector(FTFA_Type *ftfa, uint32_t address); |
mjr | 76:7f5912b6340e | 56 | void iapProgramBlock(FTFA_Type *ftfa, uint32_t address, const void *src, uint32_t length); |
mjr | 76:7f5912b6340e | 57 | } |
mjr | 76:7f5912b6340e | 58 | |
mjr | 76:7f5912b6340e | 59 | |
mjr | 2:c174f9ee414a | 60 | |
mjr | 2:c174f9ee414a | 61 | enum FCMD { |
mjr | 2:c174f9ee414a | 62 | Read1s = 0x01, |
mjr | 2:c174f9ee414a | 63 | ProgramCheck = 0x02, |
mjr | 2:c174f9ee414a | 64 | ReadResource = 0x03, |
mjr | 2:c174f9ee414a | 65 | ProgramLongword = 0x06, |
mjr | 2:c174f9ee414a | 66 | EraseSector = 0x09, |
mjr | 2:c174f9ee414a | 67 | Read1sBlock = 0x40, |
mjr | 2:c174f9ee414a | 68 | ReadOnce = 0x41, |
mjr | 2:c174f9ee414a | 69 | ProgramOnce = 0x43, |
mjr | 2:c174f9ee414a | 70 | EraseAll = 0x44, |
mjr | 2:c174f9ee414a | 71 | VerifyBackdoor = 0x45 |
mjr | 2:c174f9ee414a | 72 | }; |
mjr | 2:c174f9ee414a | 73 | |
mjr | 2:c174f9ee414a | 74 | |
mjr | 2:c174f9ee414a | 75 | /* Check if an error occured |
mjr | 2:c174f9ee414a | 76 | Returns error code or Success*/ |
mjr | 76:7f5912b6340e | 77 | static IAPCode check_error(void) |
mjr | 76:7f5912b6340e | 78 | { |
mjr | 2:c174f9ee414a | 79 | if (FTFA->FSTAT & FTFA_FSTAT_FPVIOL_MASK) { |
mjr | 2:c174f9ee414a | 80 | #ifdef IAPDEBUG |
mjr | 2:c174f9ee414a | 81 | printf("IAP: Protection violation\r\n"); |
mjr | 2:c174f9ee414a | 82 | #endif |
mjr | 2:c174f9ee414a | 83 | return ProtectionError; |
mjr | 2:c174f9ee414a | 84 | } |
mjr | 2:c174f9ee414a | 85 | if (FTFA->FSTAT & FTFA_FSTAT_ACCERR_MASK) { |
mjr | 2:c174f9ee414a | 86 | #ifdef IAPDEBUG |
mjr | 2:c174f9ee414a | 87 | printf("IAP: Flash access error\r\n"); |
mjr | 2:c174f9ee414a | 88 | #endif |
mjr | 2:c174f9ee414a | 89 | return AccessError; |
mjr | 2:c174f9ee414a | 90 | } |
mjr | 2:c174f9ee414a | 91 | if (FTFA->FSTAT & FTFA_FSTAT_RDCOLERR_MASK) { |
mjr | 2:c174f9ee414a | 92 | #ifdef IAPDEBUG |
mjr | 2:c174f9ee414a | 93 | printf("IAP: Collision error\r\n"); |
mjr | 2:c174f9ee414a | 94 | #endif |
mjr | 2:c174f9ee414a | 95 | return CollisionError; |
mjr | 2:c174f9ee414a | 96 | } |
mjr | 2:c174f9ee414a | 97 | if (FTFA->FSTAT & FTFA_FSTAT_MGSTAT0_MASK) { |
mjr | 2:c174f9ee414a | 98 | #ifdef IAPDEBUG |
mjr | 2:c174f9ee414a | 99 | printf("IAP: Runtime error\r\n"); |
mjr | 2:c174f9ee414a | 100 | #endif |
mjr | 2:c174f9ee414a | 101 | return RuntimeError; |
mjr | 2:c174f9ee414a | 102 | } |
mjr | 2:c174f9ee414a | 103 | #ifdef IAPDEBUG |
mjr | 2:c174f9ee414a | 104 | printf("IAP: No error reported\r\n"); |
mjr | 2:c174f9ee414a | 105 | #endif |
mjr | 2:c174f9ee414a | 106 | return Success; |
mjr | 76:7f5912b6340e | 107 | } |
mjr | 76:7f5912b6340e | 108 | |
mjr | 76:7f5912b6340e | 109 | IAPCode FreescaleIAP::program_flash(int address, const void *src, unsigned int length) |
mjr | 76:7f5912b6340e | 110 | { |
mjr | 76:7f5912b6340e | 111 | #ifdef IAPDEBUG |
mjr | 76:7f5912b6340e | 112 | printf("IAP: Programming flash at %x with length %d\r\n", address, length); |
mjr | 76:7f5912b6340e | 113 | #endif |
mjr | 76:7f5912b6340e | 114 | |
mjr | 76:7f5912b6340e | 115 | // Disable peripheral IRQs. Empirically, this seems to be vital to |
mjr | 76:7f5912b6340e | 116 | // getting the writing process (especially the erase step) to work |
mjr | 76:7f5912b6340e | 117 | // reliably. Even with the CPU interrupt mask off (CPSID I in the |
mjr | 76:7f5912b6340e | 118 | // assembly), it appears that peripheral interrupts will |
mjr | 76:7f5912b6340e | 119 | NVIC_DisableIRQ(PIT_IRQn); |
mjr | 76:7f5912b6340e | 120 | NVIC_DisableIRQ(I2C0_IRQn); |
mjr | 76:7f5912b6340e | 121 | NVIC_DisableIRQ(I2C1_IRQn); |
mjr | 76:7f5912b6340e | 122 | NVIC_DisableIRQ(PORTA_IRQn); |
mjr | 76:7f5912b6340e | 123 | NVIC_DisableIRQ(PORTD_IRQn); |
mjr | 76:7f5912b6340e | 124 | NVIC_DisableIRQ(USB0_IRQn); |
mjr | 76:7f5912b6340e | 125 | NVIC_DisableIRQ(TPM0_IRQn); |
mjr | 76:7f5912b6340e | 126 | NVIC_DisableIRQ(TPM1_IRQn); |
mjr | 76:7f5912b6340e | 127 | NVIC_DisableIRQ(TPM2_IRQn); |
mjr | 76:7f5912b6340e | 128 | NVIC_DisableIRQ(RTC_IRQn); |
mjr | 76:7f5912b6340e | 129 | NVIC_DisableIRQ(RTC_Seconds_IRQn); |
mjr | 76:7f5912b6340e | 130 | NVIC_DisableIRQ(LPTimer_IRQn); |
mjr | 76:7f5912b6340e | 131 | |
mjr | 76:7f5912b6340e | 132 | // presume success |
mjr | 76:7f5912b6340e | 133 | IAPCode status = Success; |
mjr | 76:7f5912b6340e | 134 | |
mjr | 76:7f5912b6340e | 135 | // I'm not 100% convinced this is 100% reliable yet. So let's show |
mjr | 76:7f5912b6340e | 136 | // some diagnostic lights while we're working. If anyone sees any |
mjr | 76:7f5912b6340e | 137 | // freezes, the lights that are left on at the freeze will tell us |
mjr | 76:7f5912b6340e | 138 | // which step is crashing. |
mjr | 76:7f5912b6340e | 139 | extern void diagLED(int,int,int); |
mjr | 76:7f5912b6340e | 140 | |
mjr | 76:7f5912b6340e | 141 | // Erase the sector(s) covered by the write. Before writing, we must |
mjr | 76:7f5912b6340e | 142 | // erase each sector that we're going to touch on the write. |
mjr | 76:7f5912b6340e | 143 | for (uint32_t ofs = 0 ; ofs < length ; ofs += SECTOR_SIZE) |
mjr | 76:7f5912b6340e | 144 | { |
mjr | 76:7f5912b6340e | 145 | // Show RED on the first sector, GREEN on second, BLUE on third. Each |
mjr | 76:7f5912b6340e | 146 | // sector is 1K, so I don't think we'll need more than 3 for the |
mjr | 76:7f5912b6340e | 147 | // foreseeable future. (RAM on the KL25Z is so tight that it will |
mjr | 76:7f5912b6340e | 148 | // probably stop us from adding enough features to require more |
mjr | 76:7f5912b6340e | 149 | // configuration variables than 3K worth.) |
mjr | 76:7f5912b6340e | 150 | diagLED(ofs/SECTOR_SIZE == 0, ofs/SECTOR_SIZE == 1, ofs/SECTOR_SIZE == 2); |
mjr | 76:7f5912b6340e | 151 | |
mjr | 76:7f5912b6340e | 152 | // erase the sector |
mjr | 76:7f5912b6340e | 153 | iapEraseSector(FTFA, address + ofs); |
mjr | 76:7f5912b6340e | 154 | } |
mjr | 76:7f5912b6340e | 155 | |
mjr | 76:7f5912b6340e | 156 | // If the erase was successful, write the data. |
mjr | 76:7f5912b6340e | 157 | if ((status = check_error()) == Success) |
mjr | 76:7f5912b6340e | 158 | { |
mjr | 76:7f5912b6340e | 159 | // show cyan while the write is in progress |
mjr | 76:7f5912b6340e | 160 | diagLED(0, 1, 1); |
mjr | 76:7f5912b6340e | 161 | |
mjr | 76:7f5912b6340e | 162 | // do the write |
mjr | 76:7f5912b6340e | 163 | iapProgramBlock(FTFA, address, src, length); |
mjr | 76:7f5912b6340e | 164 | |
mjr | 76:7f5912b6340e | 165 | // show white when the write completes |
mjr | 76:7f5912b6340e | 166 | diagLED(1, 1, 1); |
mjr | 76:7f5912b6340e | 167 | |
mjr | 76:7f5912b6340e | 168 | // check again for errors |
mjr | 76:7f5912b6340e | 169 | status = check_error(); |
mjr | 76:7f5912b6340e | 170 | } |
mjr | 76:7f5912b6340e | 171 | |
mjr | 76:7f5912b6340e | 172 | // restore peripheral IRQs |
mjr | 76:7f5912b6340e | 173 | NVIC_EnableIRQ(PIT_IRQn); |
mjr | 76:7f5912b6340e | 174 | NVIC_EnableIRQ(I2C0_IRQn); |
mjr | 76:7f5912b6340e | 175 | NVIC_EnableIRQ(I2C1_IRQn); |
mjr | 76:7f5912b6340e | 176 | NVIC_EnableIRQ(PORTA_IRQn); |
mjr | 76:7f5912b6340e | 177 | NVIC_EnableIRQ(PORTD_IRQn); |
mjr | 76:7f5912b6340e | 178 | NVIC_EnableIRQ(USB0_IRQn); |
mjr | 76:7f5912b6340e | 179 | NVIC_EnableIRQ(TPM0_IRQn); |
mjr | 76:7f5912b6340e | 180 | NVIC_EnableIRQ(TPM1_IRQn); |
mjr | 76:7f5912b6340e | 181 | NVIC_EnableIRQ(TPM2_IRQn); |
mjr | 76:7f5912b6340e | 182 | NVIC_EnableIRQ(RTC_IRQn); |
mjr | 76:7f5912b6340e | 183 | NVIC_EnableIRQ(RTC_Seconds_IRQn); |
mjr | 76:7f5912b6340e | 184 | NVIC_EnableIRQ(LPTimer_IRQn); |
mjr | 76:7f5912b6340e | 185 | |
mjr | 76:7f5912b6340e | 186 | // return the result |
mjr | 76:7f5912b6340e | 187 | return status; |
mjr | 76:7f5912b6340e | 188 | } |
mjr | 76:7f5912b6340e | 189 | |
mjr | 76:7f5912b6340e | 190 | uint32_t FreescaleIAP::flash_size(void) |
mjr | 76:7f5912b6340e | 191 | { |
mjr | 76:7f5912b6340e | 192 | uint32_t retval = (SIM->FCFG2 & 0x7F000000u) >> (24-13); |
mjr | 76:7f5912b6340e | 193 | if (SIM->FCFG2 & (1<<23)) //Possible second flash bank |
mjr | 76:7f5912b6340e | 194 | retval += (SIM->FCFG2 & 0x007F0000u) >> (16-13); |
mjr | 76:7f5912b6340e | 195 | return retval; |
mjr | 76:7f5912b6340e | 196 | } |
mjr | 76:7f5912b6340e | 197 | |
mjr | 76:7f5912b6340e | 198 | /* Check if no flash boundary is violated |
mjr | 76:7f5912b6340e | 199 | Returns true on violation */ |
mjr | 76:7f5912b6340e | 200 | bool check_boundary(int address, unsigned int length) |
mjr | 76:7f5912b6340e | 201 | { |
mjr | 76:7f5912b6340e | 202 | int temp = (address+length - 1) / SECTOR_SIZE; |
mjr | 76:7f5912b6340e | 203 | address /= SECTOR_SIZE; |
mjr | 76:7f5912b6340e | 204 | bool retval = (address != temp); |
mjr | 76:7f5912b6340e | 205 | #ifdef IAPDEBUG |
mjr | 76:7f5912b6340e | 206 | if (retval) |
mjr | 76:7f5912b6340e | 207 | printf("IAP: Boundary violation\r\n"); |
mjr | 76:7f5912b6340e | 208 | #endif |
mjr | 76:7f5912b6340e | 209 | return retval; |
mjr | 76:7f5912b6340e | 210 | } |
mjr | 76:7f5912b6340e | 211 | |
mjr | 76:7f5912b6340e | 212 | /* Check if address is correctly aligned |
mjr | 76:7f5912b6340e | 213 | Returns true on violation */ |
mjr | 76:7f5912b6340e | 214 | bool check_align(int address) |
mjr | 76:7f5912b6340e | 215 | { |
mjr | 76:7f5912b6340e | 216 | bool retval = address & 0x03; |
mjr | 76:7f5912b6340e | 217 | #ifdef IAPDEBUG |
mjr | 76:7f5912b6340e | 218 | if (retval) |
mjr | 76:7f5912b6340e | 219 | printf("IAP: Alignment violation\r\n"); |
mjr | 76:7f5912b6340e | 220 | #endif |
mjr | 76:7f5912b6340e | 221 | return retval; |
mjr | 76:7f5912b6340e | 222 | } |
mjr | 76:7f5912b6340e | 223 |