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!
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
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"
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.
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
;
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*/
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??
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
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 */
142 memcpy(buffer
, FlashAddress
, 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
;
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
;
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
;
175 for(i
=0;i
<sectors
;i
++){
176 unsigned short errorID
= writeSector(RAMPage
, RAMAddress
, details
->FlashPage
, FlashAddress
);
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??
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.
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
;
223 unsigned char currentRPage
= RPAGE
;
224 unsigned char currentPPage
= PPAGE
;
230 while (wordCount
> 0)
232 unsigned short sourceData
= *RAMSourceAddress
; /*Convert the RAMAddr to data(dereference) */
233 unsigned short errorID
= writeWord(flashDestinationAddress
, sourceData
);
238 flashDestinationAddress
++;
239 wordCount
--; /* Decrement our word counter */
243 RPAGE
= currentRPage
;
244 PPAGE
= currentPPage
;
245 // @todo TODO verify the write? necessary??
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();
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
277 // @todo TODO verify the write? necessary??