FreeEMS  0.2.0-SNAPSHOT-285-g028e24c
flashWrite.c
Go to the documentation of this file.
1 /* FreeEMS - the open source engine management system
2  *
3  * Copyright 2008-2012 Fred Cooke
4  *
5  * This file is part of the FreeEMS project.
6  *
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.
11  *
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.
16  *
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/
19  *
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!
22  *
23  * Thank you for choosing FreeEMS to run your engine!
24  */
25 
26 
27 /** @file
28  *
29  * @brief Flash manipulation functions
30  *
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.
34  *
35  * @author Sean Keys, Fred Cooke
36  */
37 
38 
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>
47 
48 
49 /** @brief Erases a sector of flash memory
50  *
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.
55  *
56  * @author Sean Keys
57  *
58  * @warning This will erase an entire 1k block starting at flashAddr
59  *
60  * @param PPage the flash page the sector is in
61  * @param flashAddr the start address of the sector
62  *
63  * @return An error code. Zero means success, anything else is a failure.
64  */
65 unsigned short eraseSector(unsigned char PPage, unsigned short *flashAddr){
66 
67  if(((unsigned short)flashAddr % flashSectorSize) != 0){
69  }
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
78 
79  // @todo TODO verify the erase, is this necessary or is it taken care of by the hardware??
80  return 0;
81 }
82 
83 
84 /** @brief Writes a block of memory to flash
85  *
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.
89  *
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 :
96  *
97  * | From Flash | From RAM | From flash |
98  * | From Flash | From RAM |
99  * | From RAM | From Flash |
100  *
101  * @warning Limited to 63k per write! (obviously)
102  *
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.
105  *
106  * @return An error code. Zero means success, anything else is a failure.
107  */
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;
114 
115  /* Check that the size isn't zero... */
116  if(details->size == 0){
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;
122 
123  /* Check for flash sector boundary crossing */
124  if((offset + details->size) > 1024){
126  }
127 
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 */
133 
134  /* Possibly three parts to copy to the buffer, copy only what is required */
135 
136  /* Save the PPAGE value and set the flash page */
137  unsigned char oldFlashPage = PPAGE;
138  PPAGE = details->FlashPage;
139 
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;
144  }
145 
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;
152 
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)));
157  }
158 
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){
165  }
166 
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;
172  }
173 
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;
179  }
180  /* Incrementing a pointer is done by blocks the size of the type, hence 512 per sector here */
181  RAMAddress += flashSectorSizeInWords;
182  FlashAddress += flashSectorSizeInWords;
183  }
184  // @todo TODO verify the write? necessary??
185  return 0;
186 }
187 
188 
189 /** @brief Writes a sector from memory to a sector in flash
190  *
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.
197  *
198  * @author Sean Keys
199  *
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
204  *
205  * @return an error code. Zero means success, anything else is a failure.
206  */
207 unsigned short writeSector(unsigned char RPage, unsigned short* RAMSourceAddress, unsigned char PPage , unsigned short* flashDestinationAddress){
208 
209  if(((unsigned short)flashDestinationAddress % flashSectorSize) != 0){
211  }
212 
213  if(((unsigned short)flashDestinationAddress) < 0x4000){
214  return addressNotFlashRegion;
215  }
216 
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 */
219 
220  unsigned short wordCount = flashSectorSizeInWords;
221 
222  /* Save pages */
223  unsigned char currentRPage = RPAGE;
224  unsigned char currentPPage = PPAGE;
225 
226  /* Switch pages */
227  RPAGE = RPage;
228  PPAGE = PPage;
229 
230  while (wordCount > 0)
231  {
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;
236  }
237  RAMSourceAddress++;
238  flashDestinationAddress++;
239  wordCount--; /* Decrement our word counter */
240  }
241 
242  /* Restore pages */
243  RPAGE = currentRPage;
244  PPAGE = currentPPage;
245  // @todo TODO verify the write? necessary??
246  return 0;
247 }
248 
249 
250 /** @brief Program Command
251  *
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();
257  *
258  * @author Sean Keys
259  *
260  * @warning Be sure your destination address is not protected or you will flag an error in FSTAT
261  *
262  * @param flashDestination where you want to write your data
263  * @param data the data you are going to write
264  *
265  * @return an error code. Zero means success, anything else is a failure.
266  */
267 unsigned short writeWord(unsigned short* flashDestination, unsigned short data){
268  if((unsigned short)flashDestination & 0x0001){
269  return addressNotWordAligned;
270  }
271 
272  FSTAT=(ACCERR | PVIOL);
273  *flashDestination = data;
274  FCMD = WORD_PROGRAM; //Load Flash Command Register With Word_Program mask
275  StackBurner();
276 
277  // @todo TODO verify the write? necessary??
278  return 0;
279 }