Added error return to block lookup, fixed bugs in small block burn, finished off...
[freeems-vanilla.git] / src / flashWrite.c
blobb3033b6019346c9fc558ff9616174780f3da7554
1 /* flashWrite.c
3 Copyright 2008 Sean Keys
5 This file is part of the FreeEMS project.
7 FreeEMS software is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 FreeEMS software is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with any FreeEMS software. If not, see <http://www.gnu.org/licenses/>.
20 We ask that if you make any changes to this file you send them upstream to us at admin@diyefi.org
22 Thank you for choosing FreeEMS to run your engine! */
24 #define FLASHWRITE_C
25 #include "inc/freeEMS.h"
26 #include "inc/flashWrite.h"
27 #include "inc/flashBurn.h"
28 #include "inc/commsISRs.h"
29 #include "inc/commsCore.h"
30 #include <string.h>
33 /**************************************************************************************************************/
34 /* 27.4.2.4 Sector Erase Command
36 The sector erase operation will erase all addresses in a 1 Kbyte sector of Flash memory using an embedded
37 algorithm.
39 An example flow to execute the sector erase operation is shown in Figure 27-29. The sector erase
40 command write sequence is as follows:
42 1. Write to a Flash block address to start the command write sequence for the sector erase command.
43 The Flash address written determines the sector to be erased while global address bits [9:0] and the
44 data written are ignored. Multiple Flash sectors can be simultaneously erased by writing to the
45 same relative address in each Flash block.
47 2. Write the sector erase command, 0x40, to the FCMD register.
49 3. Clear the CBEIF flag in the FSTAT register by writing a 1 to CBEIF to launch the sector erase
50 command.
52 If a Flash sector to be erased is in a protected area of the Flash block, the PVIOL flag in the FSTAT register
53 will set and the sector erase command will not launch. Once the sector erase command has successfully
54 launched, the CCIF flag in the FSTAT register will set after the sector erase operation has completed unless
55 a new command write sequence has been buffered. */
56 unsigned short eraseSector(unsigned char PPage, unsigned short *flashAddr){
58 if (((unsigned short)flashAddr % flashSectorSize) != 0){
59 return addressNotSectorAligned;
61 unsigned char currentPage = PPAGE;
62 PPAGE = PPage;
63 FSTAT = (PVIOL|ACCERR); /* clear any errors */
64 (*flashAddr) = 0xFFFF; /* Dummy data to first word of page to be erased it will write FFFF regardless with the erase command*/
65 PPAGE = currentPage;
66 FCMD = SECTOR_ERASE; /* set the flash command register mode to ERASE */
67 StackBurner(); //PPAGE loaded into Register B, PPAGE is set with Reg B in StackBurn asm file
68 //TODO add return for accerr and pviol error bits
70 return 0;
74 /* TODO for 0.0.19
76 * To be called from replace block of flash and ram to flash serial functions :
78 * create some sort of function to copy the flash sector up into
79 * the serial rx buffer in the high end and then over write with
80 * the small piece defined either from incoming data, or from its
81 * memory location. Then just call burn in the normal way.
83 * function could take :
84 * pointer to the buffer region (must be 1024 long or more)
85 * rpage, address, length of data to be persisted
86 * ppage, address of the sector to retrieve the rest of the data from
87 * pointer to the details object we want to use for the following call :
91 /* Pass in anything you like and it will error if it can't handle it. */
92 /* Note : Limited to 63k per write!! (obviously) */
93 /* TODO buffer, copy and do smaller regions by supplementing with data read from the block in question. TODO see above ^ */
94 //unsigned short writeBlock(unsigned char RPage, unsigned short* RAMSourceAddress, unsigned char PPage, unsigned short* flashDestinationAddr, unsigned short size){
95 unsigned short writeBlock(blockDetails* details, void* buffer){//, void* buffer /* char* short* how to handle odd byte count?? */){
96 /* because the ram version will be in an arbitrary place we need to base
97 * our positioning from the flash location. Firstly we need to ensure that
98 * it doesn't cross any sector boundaries. Then we need to find the address
99 * of the sector to be burned to. We also need to determine if there are
100 * 2 or 3 chunks of memory to be copied to the buffer, three cases exist
101 * for that :
103 * | From Flash | From RAM | From flash |
104 * | From Flash | From RAM |
105 * | From RAM | From Flash |
108 unsigned char sectors;
109 unsigned char RAMPage;
110 // FlashPage is always the one provided and is just used as is.
111 unsigned short* RAMAddress;
112 unsigned short* FlashAddress;
114 // check size isn't zero...
115 if(details->size == 0){
116 return sizeOfBlockToBurnIsZero;
117 }else if(details->size < 1024){
118 unsigned short chunkFlashAddress = (unsigned short)details->FlashAddress;
119 /* Get the offset from the start of the sector */
120 unsigned short offset = chunkFlashAddress % flashSectorSize;
122 /* Check for flash sector boundary crossing */
123 if((offset + details->size) > 1024){
124 return smallBlockCrossesSectorBoundary;
127 /* Configure the final burn variables */
128 sectors = 1; /* By definition if we are in this code there is only one */
129 RAMPage = RPAGE; /* The buffer is always in linear RAM region */
130 RAMAddress = buffer; /* Save the buffer start address */
131 FlashAddress = (unsigned short*)(chunkFlashAddress - offset); /* Get the start of the flash sector */
133 /* Possibly three parts to copy to the buffer, copy only what is required */
135 /* Save the PPAGE value and set the flash page */
136 unsigned char oldFlashPage = PPAGE;
137 PPAGE = details->FlashPage;
139 /* If the chunk doesn't start at the beginning of the sector, copy the first area from flash */
140 if(offset != 0){
141 memcpy(buffer, FlashAddress, offset);
142 buffer += offset;
145 // copy the middle section up regardless
146 unsigned char oldRAMPage = RPAGE;
147 RPAGE = details->RAMPage;
148 memcpy(buffer, details->RAMAddress, details->size);
149 buffer += details->size;
150 RPAGE = oldRAMPage;
152 // if the chunk doesn't end at the end of the sector, copy the last are from flash
153 if((offset + details->size) < 1024){ // logic dodgy ??? TODO
154 void* chunkFlashEndAddress = details->FlashAddress + details->size;
155 memcpy(buffer, chunkFlashEndAddress, (1024 - (offset + details->size)));
158 /* Restore the PPAGE value back */
159 PPAGE = oldFlashPage;
160 } else {
161 // if not smaller than 1024, check size is product of sector size
162 if((details->size % flashSectorSize) != 0){
163 return sizeNotMultipleOfSectorSize;
166 // larger chunk
167 sectors = details->size / flashSectorSize;
168 RAMPage = details->RAMPage;
169 RAMAddress = (unsigned short*)details->RAMAddress;
170 FlashAddress = (unsigned short*)details->FlashAddress;
173 unsigned char i;
174 for(i=0;i<sectors;i++){
175 unsigned short errorID = writeSector(RAMPage, RAMAddress, details->FlashPage, FlashAddress);
176 if(errorID != 0){
177 return errorID;
179 /* Incrementing a pointer is done by blocks the size of the type, hence 512 per sector here */
180 RAMAddress += flashSectorSizeInWords;
181 FlashAddress += flashSectorSizeInWords;
183 return 0;
186 // original 5 args version :
187 //if(((size % flashSectorSize) != 0) || (size == 0)){
188 // return sizeNotMultipleOfSectorSize;
191 //unsigned char sectors = size / flashSectorSize;
192 //unsigned char i;
193 //for(i=0;i<sectors;i++){
194 // unsigned short errorID = writeSector(RPage, RAMSourceAddress, PPage, flashDestinationAddr);
195 // if(errorID != 0){
196 // return errorID;
197 // }
198 // /* Incrementing a pointer is done by blocks the size of the type, hence 512 per sector here */
199 // flashDestinationAddr += flashSectorSizeInWords;
200 // RAMSourceAddress += flashSectorSizeInWords;
202 //return 0;
205 /*******************************************************************************
206 * writeSector will use writeWord to write a 1k block from sourceAddress(RAM) to flashDestinationAddress.
207 * Give it the starting memory address and the destination flash address.
208 * Both addresses will be incremented by 1 word after a successful writeWord,
209 * until the whole 1024 byte sector has been written. Before any writing occurs
210 * eraseSector is called to make sure the destination is blank. */
211 unsigned short writeSector(unsigned char RPage, unsigned short* RAMSourceAddress, unsigned char PPage , unsigned short* flashDestinationAddress){
213 if (((unsigned short)flashDestinationAddress % flashSectorSize) != 0){
214 return addressNotSectorAligned;
217 if(((unsigned short)flashDestinationAddress) < 0x4000){
218 return addressNotFlashRegion;
221 //TODO Decide if we need to disable interrupts since we are manually setting Flash/RAM pages.
222 eraseSector((unsigned char)PPage, (unsigned short*)flashDestinationAddress); /* First Erase our destination block */
224 unsigned short wordCount = flashSectorSizeInWords;
226 /* Save pages */
227 unsigned char currentRPage = RPAGE;
228 unsigned char currentPPage = PPAGE;
230 /* Switch pages */
231 RPAGE = RPage;
232 PPAGE = PPage;
234 while (wordCount > 0)
236 unsigned short sourceData = *RAMSourceAddress; /*Convert the RAMAddr to data(dereference) */
237 unsigned short errorID = writeWord(flashDestinationAddress, sourceData);
238 if(errorID != 0){
239 return errorID;
241 RAMSourceAddress++;
242 flashDestinationAddress++;
243 wordCount--; /* Decrement our word counter */
246 /* Restore pages */
247 RPAGE = currentRPage;
248 PPAGE = currentPPage;
249 return 0;
251 /* 27.4.2.3 Program Command
253 The program operation will program a previously erased word in the Flash memory using an embedded
254 algorithm.
256 An example flow to execute the program operation is shown in Figure 27-28. The program command write
257 sequence is as follows:
259 1. Write to a Flash block address to start the command write sequence for the program command. The
260 data written will be programmed to the address written. Multiple Flash blocks can be
261 simultaneously programmed by writing to the same relative address in each Flash block.
263 2. Write the program command, 0x20, to the FCMD register.
265 3. Clear the CBEIF flag in the FSTAT register by writing a 1 to CBEIF to launch the program
266 command.
268 If a word to be programmed is in a protected area of the Flash block, the PVIOL flag in the FSTAT register
269 will set and the program command will not launch. Once the program command has successfully launched,
270 the CCIF flag in the FSTAT register will set after the program operation has completed unless a new
271 command write sequence has been buffered. By executing a new program command write sequence on
272 sequential words after the CBEIF flag in the FSTAT register has been set, up to 55% faster programming
273 time per word can be effectively achieved than by waiting for the CCIF flag to set after each program
274 operation.*/
275 unsigned short writeWord(unsigned short* flashDestination, unsigned short data){
276 if ((unsigned short)flashDestination & 0x0001){
277 return addressNotWordAligned;
280 FSTAT=(ACCERR | PVIOL);
281 *flashDestination = data;
282 FCMD = WORD_PROGRAM; //Load Flash Command Register With Word_Program mask
283 StackBurner();
285 return 0;