Writing to flash programatically

(my apologies, as I started a similar thread over a year ago, but I can’t seem to find it now).

Summary: I really need use a function to write to a small flash area pragmatically, while in run mode… even just one small block of flash.

Here’s the issue. I have a WIXEL application which has 8 user accessible momentary contact buttons, along with 4 DIP switches inside my mounting case. I had hoped the 4 switches would be enough for some programming options, but its not enough… I need some more. They will have to change often, so I don’t want an end user to have to resort to re-loading the program with new settings for variables. So for now, I’ve set up the programming to detect certain button combinations when the unit is first turned on, through which the user can then alter some program behavior. The problem, of course, is that the user will have to repeat this process every time he/she turns the unit on. It would be so much better if after the user got through with the custom programming, they could press what i would call a “save” button.

I know could get myself a small I2C flash memory chip to store some variables. But PC boards have already been cut, and so I’d like to solve this pragmatically with the current version of this device if at all possible. The the best way I can think of is if some function could be created to read or write flash. Surely there must be some safe area, or at least an area that would be safe for a given application. And when you consider that even 16 bytes is the equivalent of 128 DIP switches, its obvious that just a single block of flash would be more than adequate.

I hope someone can help with this, and I’m more than sure it could benefit a lot of people’s applications. Surely in the boot loader Pololu has designed, there is an algorithm for writing the flash during program load. I know the boot loader itself is proprietary, but surely the code for that one bit of functionality could be made available for this purpose? I understand the risks, and would not hold Pololu responsible if I clobbered a Wixel by overwriting something important. Please help me do this.

Thanks in advance.

Hello.

Here is the code you requested. This was copied from the Wixel’s bootloader and hopefully it will be useful to you. You do not need to worry about erasing the bootloader on your Wixel because we have enabled write protection on the sections of flash that it occupies.

Global variables you’ll need:

  // writeBuffer holds the data from the computer that we want to write in to flash.
  XDATA uint8 writeBuffer[1024*3];

  // flashWriteDmaConfig holds the configuration of DMA channel 0, which we use to
  // transfer the data from writeBuffer in to flash.
  XDATA DMA_CONFIG flashWriteDmaConfig;

  // startFlashWrite is a small piece of code we store in RAM which initiates the flash write.
  // According to datasheet section 12.3.2.1, this code needs to be 2-aligned if it is executed
  // from flash. SDCC does not have a good way of 2-aligning code, so we choose to put the code in
  // RAM instead of flash.
  XDATA uint8 startFlashWrite[] = {
    0x75, 0xAE, 0x02,  // mov _FCTL, #2 :   Sets FCTRL.WRITE bit to 1, initiating a write to flash.
    0x22               // ret           :   Returns to the calling function.
  };

One-time setup:

	// Configure the flash write timer.  See section 12.3.5 of the datasheet.
	FWT = 32;

	// Set up the DMA configuration block we use for writing to flash (See Figure 21 of the datasheet).
	// LENL and LENH are sent later when we know how much data to write.
	flashWriteDmaConfig.SRCADDRH = (unsigned short)&writeBuffer >> 8;
	flashWriteDmaConfig.SRCADDRL = (unsigned char)&writeBuffer;
	flashWriteDmaConfig.DESTADDRH = XDATA_SFR_ADDRESS(FWDATA) >> 8;
	flashWriteDmaConfig.DESTADDRL = XDATA_SFR_ADDRESS(FWDATA);

	// WORDSIZE = 0     : byte
	// TMODE = 0        : single mode
	// TRIG = 0d18      : flash
	flashWriteDmaConfig.DC6 = 18;

	// SRCINC = 01      : yes
	// DESTINC = 00     : no
	// IRQMASK = 0      : no   *datasheet said this bit should be 1
	// M8 = 0           : 8-bit transfer
	// PRIORITY = 0b10  : high
	flashWriteDmaConfig.DC7 = 0b01000010;

	DMA0CFG = (uint16)&flashWriteDmaConfig;

Erasing a page of flash:

// To erase a page, set FADDRH and then call this function.
void eraseFlashPage()
{
	FCTL = 1;              // Set FCTL.ERASE to 1 to initiate the erasing.
	__asm nop __endasm;    // Datasheet says a NOP is necessary after the instruction that initiates the erase.
	__asm nop __endasm;    // We have extra NOPs to be safe.
	__asm nop __endasm;
	__asm nop __endasm;
	while(FCTL & 0x80){};  // Wait for erasing to be complete.
}

Writing to flash:

	FADDR = address >> 1;
	flashWriteDmaConfig.VLEN_LENH = length >> 8;
	flashWriteDmaConfig.LENL = length;
	DMAIRQ &= ~(1<<0); // Clear DMAIF0 so we can poll it to see when the transfer finishes.
	DMAARM |= (1<<0);
	__asm lcall _startFlashWrite __endasm;
 	while(!(DMAIRQ & (1<<0))){} // wait for the transfer to finish by polling DMAIF0
	while(FCTL & 0xC0){}  // wait for last word to finish writing by polling BUSY and SWBUSY

–David

David,

Thanks! This is very exciting, and I’m very anxious to try it! May i pick your brain for a few clarifications?

  1. Could you please also explain the modification required to READ a page of flash, or find me a similar example of a READ operation? I see that my WIXEL software is able to read an application as well as write it, so perhaps there is a snipet of “READ” code you could also share?

  2. I know this is just an example since its taken from the boot code. The 3K buffer is a pretty large write buffer and I’m not sure I have that much space left for it in my app. So just how big is one page, and is there any reason I couldn’t define a buffer just that size?

  3. Is it necessary for any reason to erase a page of flash before writing new data?

4)… in the one time setup, the code snippet you show says…

 flashWriteDmaConfig.DESTADDRH = XDATA_SFR_ADDRESS(FWDATA) >> 8;
 flashWriteDmaConfig.DESTADDRL = XDATA_SFR_ADDRESS(FWDATA);

I assume FWDATA needs to be set to some safe area, or I’ll clobber my own app? Or has FWDATA already been incremented as my application was loaded? I’m just looking for a way to make sure I’m either pointing beyond my application, or to another known safe area.

Hello.

  1. Flash is part of the unified XDATA memory space on the CC2511, so you can just pick an address, cast it to an XDATA pointer, and then dereference it to read it. The instructions that get generated are the same as the instructions for reading RAM variables in XDATA. For example, this code reads from address 0x1000.
uint8 value = *(uint8 XDATA *)0x1000;

You could probably make that code neater if you tell the C compiler that there is an XDATA variable at the desired address and then you just read data from the variable.

  1. A page is actually 1024 bytes. Your write buffer can be smaller than 1024 bytes; I think it could just be the same size as the data you are trying to write to flash but you might need to make it be a multiple of 2 bytes since the flash words are 16-bit. If the data is already somewhere in a contiguous block of your RAM then you could probably just use that block as your write buffer instead of making a new write buffer.

  2. You have to erase a page of flash memory before you write to it, because the “write” operation only allows you to change bits from one to zero. The erase operation changes everything back to ones.

  3. FWDATA is just the register that receives the data. The DMA is used to read your data from RAM and write it to FWDATA. You need to worry about setting FADDRH and FADDRL to a good address that will not clobber your app. I think our header files define a 16-bit SFR named FADDR so you can write to FADDRH and FADDRL at the same time as shown in the code I posted. If you set FADDR to point to topmost 1 KB of the application flash space that should be good because the SDCC compiler likes to puts its data at the bottom (lower addresses). Please refer to the “The Wixel USB Bootloader” section of the Wixel User’s Guide, which explains what sections of flash memory the application takes up.

I recommend that you read section 12.3 of the CC2511 datasheet, which explains the flash controller.

–David

Thanks Dave! I will dig into the datasheet as a supplement, but as we know all data sheets have errors that can waste weeks, so its a real help that you folks have been through some of this and have commented on the code that works, vs. the datasheet!

This all makes sense now, and I’ll let you know how it turns out. It will be such a powerful add-on, that I’d suggest you consider making a library addition for it, perhaps with a permanently reserved 1K block! Consider how many devices like TV remote controls are programmed with a similar method! You know… hold down button ‘A’ until you see LED ‘B’ flashes once, enter a code, and then the change is saved. As cumbersome as it seems, at least it can be done in a “field” situation!

FYI, The first thing I’ll want to do is allow a unique device address to be programmed by an end user, which my own communication protocol header will look for and lock into. If I put my code in the public domain, this will make it possible for someone else to use it without fear of sabotage by another person using the application. So the user will be able to permanently program in his/her own secret PIN code, and send it to all other WIXELs with a command to save it in their own flash. Security is always good with wireless devices, right? :slight_smile:

Hey Dave,

Your last message mentioned the top 1K page/block as a possible target for my flash write experiments. Indeed, my diags show my application currently ends around 0x4160, and there’s nothing but 0xFF bytes beyond that, up to 0x7800. So you must have meant the top 1K of MY application space, correct? because the bootloader discussion in your WIXEL manual suggests that the 1st 1K block and the last 2 are used by the bootloader, which looks like what I’m seeing. So probably the top 1K block of 32K that would be available to me would be from 7400 - 0x77FF, correct? And in any case, if I accidentally went beyond that area, the upper boot data/code is protected, correct?

I must say in passing that my application is already pretty complex, and I was relieved to see how much space I had left.

Yes, the block you should use would be from 0x7400 to 0x77FF. Yes, both sections of the bootloader code are protected from being erased or written to.

I am glad you still have so much space left! By the way, an easy way to tell how much space you are using would be to look at the .mem file generated by SDCC in your Wixel app’s folder.

–David

So I’ve done everything to the letter… pretty much cut paste, with the one exception that since you mentioned you must erase a block before writing, I combined the erase and write into one function, like this…

void writeToCPUFlash(uint16 address, uint16 length, uint8 bEraseOnly)
	{
	// first erase the page
	  FADDRH  = address >> 1; //
	  FCTL = 1;               // Set FCTL.ERASE to 1 to initiate the erasing.
	   __asm nop __endasm;    // Datasheet says a NOP is necessary after the instruction that initiates the erase.
	   __asm nop __endasm;    // We have extra NOPs to be safe.
	   __asm nop __endasm;
	   __asm nop __endasm;
	   while(FCTL & 0x80){};  // Wait for erasing to be complete.

	   if (bEraseOnly) return;

	   FADDR = address >> 1;  // not sure if i need to do this again.
	   flashWriteDmaConfig.VLEN_LENH = length >> 8;
	   flashWriteDmaConfig.LENL = length;
	   DMAIRQ &= ~(1<<0); // Clear DMAIF0 so we can poll it to see when the transfer finishes.
	   DMAARM |= (1<<0);
	    __asm lcall _startFlashWrite __endasm;
	    while(!(DMAIRQ & (1<<0))){} // wait for the transfer to finish by polling DMAIF0
	    while(FCTL & 0xC0){}  // wait for last word to finish writing by polling BUSY and SWBUSY

	}

I’ve also determined that as the wixel bootloader manual says, the top 2 Kb are part of the bootloader, and have ensured through diags that the 1K block before the top two, starting at 0x7400 is definitely all 0xFF, and is free. I also have the WriteBuffer set up from your code example, except that I’ve only made it 1024 bytes big. The one time setup you showed me is being done verbatim, and I assume the many commented out items (WORDSIZE =1;, etc) can remain commented out.

I’ve also filled the first 256 bytes of my writeBuffer with sequential numbers, using a simple loop.

So using my call above, if I do this…

writeToCPUFlash(0x7400, 0, 1);

All is well… or at least nothing hangs. Of course with the last item being '1", only the erase operation is being done,and since its already all FFs, I can’t prove its doing anything yet. But it does prove it is not hanging. However, when I call the routine like this…

writeToCPUFlash(0x7400, 256, 0);

Now I’d expect the erase to be followed by a WRITE of 256 bytes. But in this case the function never returns, which tells me one of the while() loops is never reaching an end state.

Also, re starting the app and using a diag to examine the data shows me that none of the data at or beyond 0x7400 has changed.

Can you shed any light? Is it possible that other setup I’ve done within the program, such as initializing the USB to permit some diags has caused an issue?

hate to keep bugging you, but being able to write to flash will definitely make this project many times more versatile, and I’d really like to make it work. Thanks in advance.

AH! My bad! I had declared the DMA_CONFIG flashWriteDmaConfig struct to be DATA instead of XDATA! No clue why this didn’t work, but as XDATA it worked fine. And i see I also wasn’t doing the erase address calc correctly, but it works now!

The only odd thing which i guess i can live with, reloading the application wipes out all of flash. I suppose it makes sense for the app loader to do that, but is there any way around that? perhaps there’s a way to tell the compiler that program space ends a page before, at 0x7000?

David…

One glitch I’d like to discuss with you. First of all, now having understood the proper address calculation I had modified my flash write function. Again, this is just a combination of the flash erase op and the write you showed me, which some steering logic.

void writeToCPUFlash(uint16 address, uint16 length, uint8 bEraseOnly)
   {
   // first erase the page
     FADDRH  = address >> 9;  // // hi byte of address / 2
    FADDRL =0;
     FCTL = 1;                      // Set FCTL.ERASE to 1 to initiate the erasing.
      __asm nop __endasm;    // Datasheet says a NOP is necessary after the instruction that initiates the erase.
      __asm nop __endasm;    // We have extra NOPs to be safe.
      __asm nop __endasm;
      __asm nop __endasm;
      while(FCTL & 0x80){};  // Wait for erasing to be complete.

      if (bEraseOnly) return;

      FADDR = address >> 1;  // not sure if i need to do this again.
      flashWriteDmaConfig.VLEN_LENH = length >> 8;
      flashWriteDmaConfig.LENL = length;
      DMAIRQ &= ~(1<<0); // Clear DMAIF0 so we can poll it to see when the transfer finishes.
      DMAARM |= (1<<0);
       __asm lcall _startFlashWrite __endasm;
       while(!(DMAIRQ & (1<<0))){} // wait for the transfer to finish by polling DMAIF0
       while(FCTL & 0xC0){}  // wait for last word to finish writing by polling BUSY and SWBUSY

   }

So this version will hang. On the other hand if I modify this call so that it will either erase or write, but never attempt to do both in the same call, and I then separate my erase and write operations by several seconds, everything works fine. So is there a minimum time that should be enforced between erase and write operations that I’m missing? It looks like the configuration utility erases all flash first, waits a little, then write the application. is there a similar procedure I should be following time wise?

Unfortunately, I don’t know why that might be happening. Instead of delaying for several seconds, you could try delaying for 30 ms and see if that works. It might also be good to figure out which of the two loops at the end of the function is hanging, and to also see if any data was successfully written.

–David

OK, no problem. I’ll just report what I’m finding for the benefit of everyone, though I’m hard pressed to explain some of this behavior.

As for the timing it turned out that no amount of delay between the erase and write helped, which told me timing wasn’t the problem. So I altered my writeToCPUFlash() call again so that when the erase flag was specified, it would just erase and return, otherwise it would just write flash and return with no erase. Next, I set up my calling code to simply make two separate calls, once to erase and one to write. Surprisingly this worked perfectly every time, even with zero delay between the calls. I examined my support code over and over and could not find a reason for this behavior. Its easy to work around, but its just curious. If I isolate a specific trigger I’ll post it.

Hey David,

I just want to thank you again for this insight and example from your bootloader, and to encourage you once again to consider packaging up this feature into a library routine. Granted, it requires a bit of careful planning to create a “user interface” out of the buttons I have on my project, use them to fill in a configuration structure, save the data to the MCUs flash, and then retrieve it the next time the application runs. But even to just save some program state information, which you could then “resume” on a future run: Do you realize what a game changer it is? For any MCU application to be runtime “field programmable”, or retain program states without the user having to to reload code (and stuff a bunch of param_variables) is an amazing advantage. Maybe I’m exaggerating, but to me this is simply huge!

For me, not only can my transmitter application now retain a boat load of custom configuration, but with a little planning a command can be set to multiple receiver apps, which will then also be able save customizations in flash. Just the security advantage alone is noteworthy! I can now field configure a unique pattern on the header of a communication packet, and send a special command to all receivers to save and record itit. From that day forward, these receiver apps are secure, and can never be hijacked by anyone running a clone of my application, even if i make the app public domain. That may be unlikely, but security is a major issue these days, and its only one of thousands of possibilities this opens up.

Anyway, that’s just my 2¢ . I’m sure I’ll use it in all my current and future apps, whether in the wixel, or via my own implementation imbedding a CC2511F32 eventually. But I guarantee that a library feature to pragmatically write to a reserved block of flash, and then retrieve that on subsequent runs. will make a lot of lights turn on in a lot of your customer’s minds. please think about standardizing it!

Hey Dave,

Although this is an old thread, its part of the same topic. Please forgive me if we’ve covered this before. Recall that the top 2 Kb are part of the Wixel" bootloader, and that I have permanently decided that the 1K block before the top two (flash mem starting at 0x7400), is an area I have set aside for permanent non-volitile application memory. This has become a valuable resource, both for configuration and for saving some runtime “learned behavior”. The problem is, It has become a nuisance to replace all that stored data every time a new version of the app is reloaded. What could be done to avoid this issue? Some thoughts come to mind…

  1. If the safeguarded area of memory limit are checked within the bootloader itself, then it would seem that as little as a one byte change to the bootloader’s 8051 code might safeguard from 0x7400 instead of the current 0x7800. Since I now have the tools I need to read/erase/write to arbitrary blocks of flash, could I not read the block from the existing bootloader that contains this limit, alter it, and write it back? All at my own risk of course. This would be a pain, but I’d only have to do it once per wixel, and then the code to make the change could be backed out, and the regular application installed.

  2. I’m doing all my coding on Windows XP and Windows 7 boxes. If the safeguarded area is controlled completely within the PC support files (wixelcmd.exe, wixel_usb_bootloader.dll, or perhaps some registry entries), altering one of these would be a much better solution, especially if the changes only involved the registry. (Surely the numbers aren’t hard coded? )

  3. As a last resort, I guess I could read the data stored in my 1K block and export it to a serial port based monitor program, and then put it back later. But this would obviously be a major “Rupe-Goldberg” method, which at best would still waste valuable Wixel code space to support.

Thoughts?

Hello. Would it be good enough if every time you loaded the app, that 1 KB block of configuration data was reverted to some reasonable defaults? If so, then I would recommend putting those defaults into your program source code so that they get included in the HEX file.

The Wixel software has no option to preserve certain regions of flash. If you need the existing configuration data to be preserved when loading an app, then I would recommend writing a custom script on your computer that uses WixelCmd to read the configuration data from the Wixel, combines that with the HEX file for your compiled program, and then writes it to the Wixel using WixelCmd.

It is not possible to modify the bootloader from within an application because it is write-protected.

–David

I already have defaults placed when the program starts. In fact those defaults are always available to the user to implement a kind of “factory reset”. But it would be nice to retain the values, and what you described seems like an excellent idea! Thanks for that!

As cumbersome as it seems, at least it can be done in a “field” situation!

If you set FADDR to point to topmost 1 KB of the application flash space that should be good because the SDCC compiler likes to puts its data at the bottom

Hey Dave, I’d finally like to try and implement your idea. To refresh, i wanted to preserve a 1K block from 0x7400 where I save some cofig data at run time, when I load a new version of the app. You suggested I write a script to export my app’s image from flash to a hex file, combine it with my latest app version (after ensuring it doesn’t cross into 0x7400), and then uploading the new hex image. I assume this is because wixelcmd can’t just load a partial image, or you might have suggested I just isolate the 1K block. But it sounded like a reasonable workaround.

So anyway, the 0x7400 area of my exported .hex file is very obvious, but I could use a little guidance combining the files. First of all, would it be OK to simply add the hex block to the end of an existing hex or WXL file, just before the final “end mark” (:00000001FF). It would be a lot easier than trying to match up all the addresses of partly used blocks that the sdk generates. So if I just add it to the end, as long as the file is processed sequentiall, and boot loader doesn’t care if blocks overwrite each other, it should work, right? I understand that the flash is only erased once and that flash WRITEs only can generate zero bits. But this is a confirmed blank area, so nothing before this point could have written any zero bits.

Hello. I think that plan should work. If two different HEX file lines specify the same place in memory, the line that is lower in the file will overwrite the other line. The overwriting happens as wixelcmd reads the HEX file, so your uploads will not be slower or have extra writes to the actual flash of the Wixel.

–David