Add new build-all directory to ignore file. Add one missing firmware type to test...
[freeems-vanilla.git] / src / flashWrite.c
blob8aef036478a9ac4ea283e343fca858c5820f0092
1 /* FreeEMS - the open source engine management system
3 * Copyright 2008-2012 Sean Keys, Fred Cooke
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 email them upstream to
21 * us at admin(at)diyefi(dot)org or, even better, fork the code on github.com!
23 * Thank you for choosing FreeEMS to run your engine!
27 /** @file
29 * @brief Flash manipulation functions
31 * This file contains all functions that operate directly or indirectly and
32 * only on flash memory. They are used for erasing data from and reprogramming
33 * data to the embedded flash non-volatile storage area.
35 * @author Sean Keys, Fred Cooke
39 #define FLASHWRITE_C
40 #include "inc/freeEMS.h"
41 #include "inc/utils.h"
42 #include "inc/flashWrite.h"
43 #include "inc/flashBurn.h"
44 #include "inc/commsISRs.h"
45 #include "inc/commsCore.h"
46 #include <string.h>
49 /** @brief Erases a sector of flash memory
51 * This will erase a 1k sector in flash. Write 0xFFFF to the starting sector
52 * to be erased, 0xFFFF will be written regardless. Register the flash sector
53 * erase command(0x40) and call StackBurner();. If you try to erase a protected
54 * sector you will get PVIOL in the FSTAT register.
56 * @author Sean Keys
58 * @warning This will erase an entire 1k block starting at flashAddr
60 * @param PPage the flash page the sector is in
61 * @param flashAddr the start address of the sector
63 * @return An error code. Zero means success, anything else is a failure.
65 unsigned short eraseSector(unsigned char PPage, unsigned short *flashAddr){
67 if(((unsigned short)flashAddr % flashSectorSize) != 0){
68 return addressNotSectorAligned;
70 unsigned char currentPage = PPAGE;
71 PPAGE = PPage;
72 FSTAT = (PVIOL|ACCERR); /* clear any errors */
73 (*flashAddr) = 0xFFFF; /* Dummy data to first word of page to be erased it will write FFFF regardless with the erase command*/
74 PPAGE = currentPage;
75 FCMD = SECTOR_ERASE; /* set the flash command register mode to ERASE */
76 StackBurner(); //PPAGE loaded into Register B, PPAGE is set with Reg B in StackBurn asm file
77 //TODO add return for accerr and pviol error bits
79 // @todo TODO verify the erase, is this necessary or is it taken care of by the hardware??
80 return 0;
84 /** @brief Writes a block of memory to flash
86 * The block size must either be under 1024, or an exact multiple of 1024.
87 * Additionally, if under 1024 the destination should be within a single flash
88 * sector, and if a multiple of 1024, the destination should be sector aligned.
90 * Because the RAM version will be in an arbitrary place we need to base
91 * our positioning from the flash location. Firstly we need to ensure that
92 * it doesn't cross any sector boundaries. Then we need to find the address
93 * of the sector to be burned to. We also need to determine if there are
94 * 2 or 3 chunks of memory to be copied to the buffer, three cases exist
95 * for that :
97 * | From Flash | From RAM | From flash |
98 * | From Flash | From RAM |
99 * | From RAM | From Flash |
101 * @warning Limited to 63k per write! (obviously)
103 * @param details contains the RAM address and page to be read from, the flash address and page to be burned to and the size to be read.
104 * @param buffer is a pointer to a block of RAM at least 1024 bytes long used to allow small chunks to be burned independently.
106 * @return An error code. Zero means success, anything else is a failure.
108 unsigned short writeBlock(blockDetails* details, void* buffer){
109 unsigned char sectors;
110 unsigned char RAMPage;
111 /* FlashPage is always the one provided and is just used as is. */
112 unsigned short* RAMAddress;
113 unsigned short* FlashAddress;
115 /* Check that the size isn't zero... */
116 if(details->size == 0){
117 return sizeOfBlockToBurnIsZero;
118 }else if(details->size < 1024){
119 unsigned short chunkFlashAddress = (unsigned short)details->FlashAddress;
120 /* Get the offset from the start of the sector */
121 unsigned short offset = chunkFlashAddress % flashSectorSize;
123 /* Check for flash sector boundary crossing */
124 if((offset + details->size) > 1024){
125 return smallBlockCrossesSectorBoundary;
128 /* Configure the final burn variables */
129 sectors = 1; /* By definition if we are in this code there is only one */
130 RAMPage = RPAGE; /* The buffer is always in linear RAM region */
131 RAMAddress = buffer; /* Save the buffer start address */
132 FlashAddress = (unsigned short*)(chunkFlashAddress - offset); /* Get the start of the flash sector */
134 /* Possibly three parts to copy to the buffer, copy only what is required */
136 /* Save the PPAGE value and set the flash page */
137 unsigned char oldFlashPage = PPAGE;
138 PPAGE = details->FlashPage;
140 /* If the chunk doesn't start at the beginning of the sector, copy the first area from flash */
141 if(offset != 0){
142 memcpy(buffer, FlashAddress, offset);
143 buffer += offset;
146 /* Copy the middle section up regardless */
147 unsigned char oldRAMPage = RPAGE;
148 RPAGE = details->RAMPage;
149 memcpy(buffer, details->RAMAddress, details->size);
150 buffer += details->size;
151 RPAGE = oldRAMPage;
153 /* If the chunk doesn't end at the end of the sector, copy the last are from flash */
154 if((offset + details->size) < 1024){
155 void* chunkFlashEndAddress = details->FlashAddress + details->size;
156 memcpy(buffer, chunkFlashEndAddress, (1024 - (offset + details->size)));
159 /* Restore the PPAGE value back */
160 PPAGE = oldFlashPage;
161 } else {
162 /* If not smaller than 1024, check size is product of sector size */
163 if((details->size % flashSectorSize) != 0){
164 return sizeNotMultipleOfSectorSize;
167 /* Set the variables to what they would have been before */
168 sectors = details->size / flashSectorSize;
169 RAMPage = details->RAMPage;
170 RAMAddress = (unsigned short*)details->RAMAddress;
171 FlashAddress = (unsigned short*)details->FlashAddress;
174 unsigned char i;
175 for(i=0;i<sectors;i++){
176 unsigned short errorID = writeSector(RAMPage, RAMAddress, details->FlashPage, FlashAddress);
177 if(errorID != 0){
178 return errorID;
180 /* Incrementing a pointer is done by blocks the size of the type, hence 512 per sector here */
181 RAMAddress += flashSectorSizeInWords;
182 FlashAddress += flashSectorSizeInWords;
184 // @todo TODO verify the write? necessary??
185 return 0;
189 /** @brief Writes a sector from memory to a sector in flash
191 * Uses writeWord to write a 1k block from sourceAddress(RAM) to
192 * flashDestinationAddress, one word at a time. Give it the starting memory
193 * address and the destination flash address. Both addresses will be
194 * incremented by 1 word after a successful writeWord, until the whole 1024
195 * byte sector has been written. Before any writing occurs eraseSector is
196 * called to make sure the destination is blank.
198 * @author Sean Keys
200 * @param RPage the page of RAM the RAMSourceAddress is located
201 * @param RAMSourceAddress the address of the source data
202 * @param PPage the page of flash where your flashDestinationAddress is located
203 * @param flashDestinationAddress where your data will be written to in flash
205 * @return an error code. Zero means success, anything else is a failure.
207 unsigned short writeSector(unsigned char RPage, unsigned short* RAMSourceAddress, unsigned char PPage , unsigned short* flashDestinationAddress){
209 if(((unsigned short)flashDestinationAddress % flashSectorSize) != 0){
210 return addressNotSectorAligned;
213 if(((unsigned short)flashDestinationAddress) < 0x4000){
214 return addressNotFlashRegion;
217 /// @todo TODO Decide if we need to disable interrupts since we are manually setting Flash/RAM pages.
218 eraseSector((unsigned char)PPage, (unsigned short*)flashDestinationAddress); /* First Erase our destination block */
220 unsigned short wordCount = flashSectorSizeInWords;
222 /* Save pages */
223 unsigned char currentRPage = RPAGE;
224 unsigned char currentPPage = PPAGE;
226 /* Switch pages */
227 RPAGE = RPage;
228 PPAGE = PPage;
230 while (wordCount > 0)
232 unsigned short sourceData = *RAMSourceAddress; /*Convert the RAMAddr to data(dereference) */
233 unsigned short errorID = writeWord(flashDestinationAddress, sourceData);
234 if(errorID != 0){
235 return errorID;
237 RAMSourceAddress++;
238 flashDestinationAddress++;
239 wordCount--; /* Decrement our word counter */
242 /* Restore pages */
243 RPAGE = currentRPage;
244 PPAGE = currentPPage;
245 // @todo TODO verify the write? necessary??
246 return 0;
250 /** @brief Program Command
252 * This will write 1 word to an empty(0xFFFF) flash address. If you try to
253 * write to an address containing data(not 0xFFFF),an error will register at
254 * FSTAT. The embedded algorithm works like this, just write to the desired
255 * flash address as you would any other writable address. Then register the
256 * program command(0x20) at FCDM, the rest is handled by StackBurner();
258 * @author Sean Keys
260 * @warning Be sure your destination address is not protected or you will flag an error in FSTAT
262 * @param flashDestination where you want to write your data
263 * @param data the data you are going to write
265 * @return an error code. Zero means success, anything else is a failure.
267 unsigned short writeWord(unsigned short* flashDestination, unsigned short data){
268 if((unsigned short)flashDestination & 0x0001){
269 return addressNotWordAligned;
272 FSTAT=(ACCERR | PVIOL);
273 *flashDestination = data;
274 FCMD = WORD_PROGRAM; //Load Flash Command Register With Word_Program mask
275 StackBurner();
277 // @todo TODO verify the write? necessary??
278 return 0;