Mirror with some correction
Dependencies: mbed FastIO FastPWM USBDevice
FreescaleIAP/FreescaleIAP.cpp@77:0b96f6867312, 2017-03-17 (annotated)
- Committer:
- mjr
- Date:
- Fri Mar 17 22:02:08 2017 +0000
- Revision:
- 77:0b96f6867312
- Parent:
- 76:7f5912b6340e
- Child:
- 79:682ae3171a08
New memory pool management; keeping old ones as #ifdefs for now for reference.
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 | 77:0b96f6867312 | 18 | // The KL25Z has an important restriction on flash writing that makes it |
mjr | 77:0b96f6867312 | 19 | // very delicate. Specifically, the flash controller (FTFA) doesn't allow |
mjr | 77:0b96f6867312 | 20 | // any read operations while a sector erase is in progress. This complicates |
mjr | 77:0b96f6867312 | 21 | // things for a KL25Z app because all program code is stored in flash by |
mjr | 77:0b96f6867312 | 22 | // default. This means that every instruction fetch is a flash read. The |
mjr | 77:0b96f6867312 | 23 | // FTFA's response to a read while an erase is in progress is to fail the |
mjr | 77:0b96f6867312 | 24 | // read. When the read is actually an instruction fetch, this results in |
mjr | 77:0b96f6867312 | 25 | // CPU lockup. Making this even more complicated, the erase operation can |
mjr | 77:0b96f6867312 | 26 | // only operate on a whole sector at a time, which takes on the order of |
mjr | 77:0b96f6867312 | 27 | // milliseconds, which is a very long time for the CPU to go without any |
mjr | 77:0b96f6867312 | 28 | // instruction fetches. Even if the code that initiates the erase is |
mjr | 77:0b96f6867312 | 29 | // located in RAM and is very careful to loop within the RAM code block, |
mjr | 77:0b96f6867312 | 30 | // any interrupt could take us out of the RAM loop and trigger a fetch |
mjr | 77:0b96f6867312 | 31 | // on a flash location. |
mjr | 76:7f5912b6340e | 32 | // |
mjr | 76:7f5912b6340e | 33 | // We use two strategies to avoid flash fetches while we're working. |
mjr | 76:7f5912b6340e | 34 | // First, the code that performs all of the FTFA operations is written |
mjr | 76:7f5912b6340e | 35 | // in assembly, in a module AREA marked READWRITE. This forces the |
mjr | 76:7f5912b6340e | 36 | // linker to put the code in RAM. The code could otherwise just have |
mjr | 76:7f5912b6340e | 37 | // well been written in C++, but as far as I know there's no way to tell |
mjr | 76:7f5912b6340e | 38 | // the mbed C++ compiler to put code in RAM. Since the FTFA code is all |
mjr | 77:0b96f6867312 | 39 | // in RAM, it doesn't by itself trigger any flash fetches as it executes, |
mjr | 77:0b96f6867312 | 40 | // so we're left with interrupts as the only concern. Second, we explicitly |
mjr | 77:0b96f6867312 | 41 | // disable all of the peripheral interrupts that we use anywhere in the |
mjr | 77:0b96f6867312 | 42 | // program (USB, all the timers, GPIO ports, etc) via the NVIC. From |
mjr | 77:0b96f6867312 | 43 | // testing, it's clear that disabling interrupts at the CPU level via |
mjr | 77:0b96f6867312 | 44 | // __disable_irq() (or the equivalent assembly instruction CPSID I) isn't |
mjr | 77:0b96f6867312 | 45 | // enough. We have to turn interrupts off at the peripheral (NVIC) level. |
mjr | 77:0b96f6867312 | 46 | // I'm really not sure why this is required, since you'd think the CPSID I |
mjr | 77:0b96f6867312 | 47 | // masking would be enough, but experimentally it's clearly not. This is |
mjr | 77:0b96f6867312 | 48 | // a detail of ARM hardware architecture that I need to look into more, |
mjr | 77:0b96f6867312 | 49 | // since it leaves me uneasy that there might be even more subtleties |
mjr | 77:0b96f6867312 | 50 | // left to uncover. But at least things seem very stable after blocking |
mjr | 77:0b96f6867312 | 51 | // interrupts at the NVIC level. |
mjr | 76:7f5912b6340e | 52 | |
mjr | 2:c174f9ee414a | 53 | #include "FreescaleIAP.h" |
mjr | 2:c174f9ee414a | 54 | |
mjr | 2:c174f9ee414a | 55 | //#define IAPDEBUG |
mjr | 76:7f5912b6340e | 56 | |
mjr | 76:7f5912b6340e | 57 | // assembly interface |
mjr | 76:7f5912b6340e | 58 | extern "C" { |
mjr | 76:7f5912b6340e | 59 | void iapEraseSector(FTFA_Type *ftfa, uint32_t address); |
mjr | 76:7f5912b6340e | 60 | void iapProgramBlock(FTFA_Type *ftfa, uint32_t address, const void *src, uint32_t length); |
mjr | 76:7f5912b6340e | 61 | } |
mjr | 76:7f5912b6340e | 62 | |
mjr | 76:7f5912b6340e | 63 | |
mjr | 2:c174f9ee414a | 64 | |
mjr | 2:c174f9ee414a | 65 | enum FCMD { |
mjr | 2:c174f9ee414a | 66 | Read1s = 0x01, |
mjr | 2:c174f9ee414a | 67 | ProgramCheck = 0x02, |
mjr | 2:c174f9ee414a | 68 | ReadResource = 0x03, |
mjr | 2:c174f9ee414a | 69 | ProgramLongword = 0x06, |
mjr | 2:c174f9ee414a | 70 | EraseSector = 0x09, |
mjr | 2:c174f9ee414a | 71 | Read1sBlock = 0x40, |
mjr | 2:c174f9ee414a | 72 | ReadOnce = 0x41, |
mjr | 2:c174f9ee414a | 73 | ProgramOnce = 0x43, |
mjr | 2:c174f9ee414a | 74 | EraseAll = 0x44, |
mjr | 2:c174f9ee414a | 75 | VerifyBackdoor = 0x45 |
mjr | 2:c174f9ee414a | 76 | }; |
mjr | 2:c174f9ee414a | 77 | |
mjr | 2:c174f9ee414a | 78 | |
mjr | 2:c174f9ee414a | 79 | /* Check if an error occured |
mjr | 2:c174f9ee414a | 80 | Returns error code or Success*/ |
mjr | 76:7f5912b6340e | 81 | static IAPCode check_error(void) |
mjr | 76:7f5912b6340e | 82 | { |
mjr | 2:c174f9ee414a | 83 | if (FTFA->FSTAT & FTFA_FSTAT_FPVIOL_MASK) { |
mjr | 2:c174f9ee414a | 84 | #ifdef IAPDEBUG |
mjr | 2:c174f9ee414a | 85 | printf("IAP: Protection violation\r\n"); |
mjr | 2:c174f9ee414a | 86 | #endif |
mjr | 2:c174f9ee414a | 87 | return ProtectionError; |
mjr | 2:c174f9ee414a | 88 | } |
mjr | 2:c174f9ee414a | 89 | if (FTFA->FSTAT & FTFA_FSTAT_ACCERR_MASK) { |
mjr | 2:c174f9ee414a | 90 | #ifdef IAPDEBUG |
mjr | 2:c174f9ee414a | 91 | printf("IAP: Flash access error\r\n"); |
mjr | 2:c174f9ee414a | 92 | #endif |
mjr | 2:c174f9ee414a | 93 | return AccessError; |
mjr | 2:c174f9ee414a | 94 | } |
mjr | 2:c174f9ee414a | 95 | if (FTFA->FSTAT & FTFA_FSTAT_RDCOLERR_MASK) { |
mjr | 2:c174f9ee414a | 96 | #ifdef IAPDEBUG |
mjr | 2:c174f9ee414a | 97 | printf("IAP: Collision error\r\n"); |
mjr | 2:c174f9ee414a | 98 | #endif |
mjr | 2:c174f9ee414a | 99 | return CollisionError; |
mjr | 2:c174f9ee414a | 100 | } |
mjr | 2:c174f9ee414a | 101 | if (FTFA->FSTAT & FTFA_FSTAT_MGSTAT0_MASK) { |
mjr | 2:c174f9ee414a | 102 | #ifdef IAPDEBUG |
mjr | 2:c174f9ee414a | 103 | printf("IAP: Runtime error\r\n"); |
mjr | 2:c174f9ee414a | 104 | #endif |
mjr | 2:c174f9ee414a | 105 | return RuntimeError; |
mjr | 2:c174f9ee414a | 106 | } |
mjr | 2:c174f9ee414a | 107 | #ifdef IAPDEBUG |
mjr | 2:c174f9ee414a | 108 | printf("IAP: No error reported\r\n"); |
mjr | 2:c174f9ee414a | 109 | #endif |
mjr | 2:c174f9ee414a | 110 | return Success; |
mjr | 76:7f5912b6340e | 111 | } |
mjr | 76:7f5912b6340e | 112 | |
mjr | 76:7f5912b6340e | 113 | IAPCode FreescaleIAP::program_flash(int address, const void *src, unsigned int length) |
mjr | 76:7f5912b6340e | 114 | { |
mjr | 76:7f5912b6340e | 115 | #ifdef IAPDEBUG |
mjr | 76:7f5912b6340e | 116 | printf("IAP: Programming flash at %x with length %d\r\n", address, length); |
mjr | 76:7f5912b6340e | 117 | #endif |
mjr | 77:0b96f6867312 | 118 | |
mjr | 76:7f5912b6340e | 119 | // presume success |
mjr | 76:7f5912b6340e | 120 | IAPCode status = Success; |
mjr | 76:7f5912b6340e | 121 | |
mjr | 76:7f5912b6340e | 122 | // I'm not 100% convinced this is 100% reliable yet. So let's show |
mjr | 76:7f5912b6340e | 123 | // some diagnostic lights while we're working. If anyone sees any |
mjr | 76:7f5912b6340e | 124 | // freezes, the lights that are left on at the freeze will tell us |
mjr | 76:7f5912b6340e | 125 | // which step is crashing. |
mjr | 76:7f5912b6340e | 126 | extern void diagLED(int,int,int); |
mjr | 76:7f5912b6340e | 127 | |
mjr | 76:7f5912b6340e | 128 | // Erase the sector(s) covered by the write. Before writing, we must |
mjr | 76:7f5912b6340e | 129 | // erase each sector that we're going to touch on the write. |
mjr | 76:7f5912b6340e | 130 | for (uint32_t ofs = 0 ; ofs < length ; ofs += SECTOR_SIZE) |
mjr | 76:7f5912b6340e | 131 | { |
mjr | 76:7f5912b6340e | 132 | // Show RED on the first sector, GREEN on second, BLUE on third. Each |
mjr | 76:7f5912b6340e | 133 | // sector is 1K, so I don't think we'll need more than 3 for the |
mjr | 76:7f5912b6340e | 134 | // foreseeable future. (RAM on the KL25Z is so tight that it will |
mjr | 76:7f5912b6340e | 135 | // probably stop us from adding enough features to require more |
mjr | 76:7f5912b6340e | 136 | // configuration variables than 3K worth.) |
mjr | 76:7f5912b6340e | 137 | diagLED(ofs/SECTOR_SIZE == 0, ofs/SECTOR_SIZE == 1, ofs/SECTOR_SIZE == 2); |
mjr | 76:7f5912b6340e | 138 | |
mjr | 76:7f5912b6340e | 139 | // erase the sector |
mjr | 76:7f5912b6340e | 140 | iapEraseSector(FTFA, address + ofs); |
mjr | 76:7f5912b6340e | 141 | } |
mjr | 76:7f5912b6340e | 142 | |
mjr | 76:7f5912b6340e | 143 | // If the erase was successful, write the data. |
mjr | 76:7f5912b6340e | 144 | if ((status = check_error()) == Success) |
mjr | 76:7f5912b6340e | 145 | { |
mjr | 76:7f5912b6340e | 146 | // show cyan while the write is in progress |
mjr | 76:7f5912b6340e | 147 | diagLED(0, 1, 1); |
mjr | 76:7f5912b6340e | 148 | |
mjr | 76:7f5912b6340e | 149 | // do the write |
mjr | 76:7f5912b6340e | 150 | iapProgramBlock(FTFA, address, src, length); |
mjr | 76:7f5912b6340e | 151 | |
mjr | 77:0b96f6867312 | 152 | // purple when done |
mjr | 77:0b96f6867312 | 153 | diagLED(1, 0, 1); |
mjr | 76:7f5912b6340e | 154 | |
mjr | 76:7f5912b6340e | 155 | // check again for errors |
mjr | 76:7f5912b6340e | 156 | status = check_error(); |
mjr | 76:7f5912b6340e | 157 | } |
mjr | 76:7f5912b6340e | 158 | |
mjr | 76:7f5912b6340e | 159 | // return the result |
mjr | 76:7f5912b6340e | 160 | return status; |
mjr | 76:7f5912b6340e | 161 | } |
mjr | 76:7f5912b6340e | 162 | |
mjr | 76:7f5912b6340e | 163 | uint32_t FreescaleIAP::flash_size(void) |
mjr | 76:7f5912b6340e | 164 | { |
mjr | 76:7f5912b6340e | 165 | uint32_t retval = (SIM->FCFG2 & 0x7F000000u) >> (24-13); |
mjr | 77:0b96f6867312 | 166 | if (SIM->FCFG2 & (1<<23)) // Possible second flash bank |
mjr | 76:7f5912b6340e | 167 | retval += (SIM->FCFG2 & 0x007F0000u) >> (16-13); |
mjr | 76:7f5912b6340e | 168 | return retval; |
mjr | 76:7f5912b6340e | 169 | } |
mjr | 76:7f5912b6340e | 170 | |
mjr | 76:7f5912b6340e | 171 | /* Check if no flash boundary is violated |
mjr | 76:7f5912b6340e | 172 | Returns true on violation */ |
mjr | 76:7f5912b6340e | 173 | bool check_boundary(int address, unsigned int length) |
mjr | 76:7f5912b6340e | 174 | { |
mjr | 76:7f5912b6340e | 175 | int temp = (address+length - 1) / SECTOR_SIZE; |
mjr | 76:7f5912b6340e | 176 | address /= SECTOR_SIZE; |
mjr | 76:7f5912b6340e | 177 | bool retval = (address != temp); |
mjr | 76:7f5912b6340e | 178 | #ifdef IAPDEBUG |
mjr | 76:7f5912b6340e | 179 | if (retval) |
mjr | 76:7f5912b6340e | 180 | printf("IAP: Boundary violation\r\n"); |
mjr | 76:7f5912b6340e | 181 | #endif |
mjr | 76:7f5912b6340e | 182 | return retval; |
mjr | 76:7f5912b6340e | 183 | } |
mjr | 76:7f5912b6340e | 184 | |
mjr | 76:7f5912b6340e | 185 | /* Check if address is correctly aligned |
mjr | 76:7f5912b6340e | 186 | Returns true on violation */ |
mjr | 76:7f5912b6340e | 187 | bool check_align(int address) |
mjr | 76:7f5912b6340e | 188 | { |
mjr | 76:7f5912b6340e | 189 | bool retval = address & 0x03; |
mjr | 76:7f5912b6340e | 190 | #ifdef IAPDEBUG |
mjr | 76:7f5912b6340e | 191 | if (retval) |
mjr | 76:7f5912b6340e | 192 | printf("IAP: Alignment violation\r\n"); |
mjr | 76:7f5912b6340e | 193 | #endif |
mjr | 76:7f5912b6340e | 194 | return retval; |
mjr | 76:7f5912b6340e | 195 | } |
mjr | 76:7f5912b6340e | 196 |