Writing to CC2511 flash memory for persistent storage?

Hi,

I’d like to have a string stored even when power is removed, and thought the CC2511 flash memory would do this. The data sheet says most XDATA locations are in flash, but is there any way to force a variable to be placed in there? I’ve done a quick test and after a reset the contents of the memory are gone. I declared it as

char XDATA description[32];

Or should I be using an external EEPROM chip? I only want to store about 128 bytes of info, at most, so a separate chip seems silly.

Regards,
David.

Hello, David.

I am not sure what sentence you found in the datasheet, but I think you are misinterpreting it. If you declare a variable using “XDATA”, it will be stored in the “external RAM”, not flash. I think you can easily put a string in flash by doing something like this:

CODE char mystring[] = "hello world";

(If that doesn’t work, try replacing CODE with “const” or “const CODE”.)

If you want to have a string that your program can change on the fly, it will be harder. Unfortunately, we do not have a nice library for doing it, but you can read, erase, and write blocks from the flash on the CC2511. I recommend reading the CC2511 datasheet for information about how to do that, and feel free to ask here when you get stuck.

–David

Hi,

I do use the CODE qualifier for my constants, but as you guessed I want to be able to write to this storage.

Looking at figure 16 on page 42 of the datasheet, am I correct in saying the flash memory is from addresses 0x0000 - 0x7fff? If so:

  1. The boot loader is from 0x0000 - 0x03FF, and 0x7800 - 0x7FFF (1st and last two pages of flash).
  2. Applications are written starting at 0x0400, and all CODE stuff is in a contiguous block starting from there.
  3. I can use flash pages from the 3rd last, counting downwards to store my info as long as my CODE doesn’t take all 29kb.

My interpretation of the datasheet is that I have to first erase a whole 1kb page if I want to write to flash. So I’d erase say the 3rd last block and then set up a DMA transfer to put the info info there. Then I can read the info out by setting a pointer to the beginning of the block:

uint8* ptr = 0x7400; //to read from the 3rd last block of flash.

If all that is correct the only thing I don’t know how to do is to ensure the condition in 12.3.2.1 pp83 that the instruction to trigger the DMA is aligned on a two byte boundary. Is there a directive I can put in C code to force this?

Regards,
David.

Everything you said up to the last paragraph is true. As far as I know, SDCC does not have a good way of aligning code. That would be very useful, not only here but also in making well-defined delay loops. However, section 12.3.2.1 of the datasheet says:

The bold in that quote was added by me. So the solution is to put a little bit of code into RAM and run that when you need to trigger the DMA. I don’t have a great way of putting functions into RAM, but you just need one tiny function so I “assembled” it by hand:

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.
};

Then you can call it with:

__asm lcall _startFlashWrite __endasm;

–David

Thanks David, very cool.

I did understand the caveat about the alignment being required only for code in FLASH, but I didn’t know how to ensure code was in RAM. It didn’t occur to me to just put the bytes of the funciton into a byte array. Something I don’t get, and didn’t understand in the datasheet either, is how the alignment is ensured. Are vars just alinged on 2-byte boundaries? I don’t see any alignment directives in your code or the datasheet asm.

I’m enjoying using the wixel, it’s a very interesting little device, and the SDK libs make it really easy to program for. It wouldn’t be quite so much fun if we had to program the bare metal, unless getting paid for it of course :slight_smile:

Makes a nice change from enterprise Java apps.

Regards,
David.

No, variables are not necessarily aligned on 2-byte boundaries. Why do you need to ensure that?

Yeah, I am not sure how the assembly code in the datasheet gets aligned. There is probably something about IAR that we don’t know.

I am glad that you enjoy the libraries in the SDK. Unfortunately, you will have to do some bare-metal programming to get the flash writing to work.

–David

I forgot, because the function is not in flash it doesn’t have the alignment requirement.

Reading through the SDCC and SDAS docs I found the inline asm keyword and assembler directives. This might work:

void tiggerDMA() {
	__asm

	.even
	ORL _FCTL, #0x02;

	__endasm;
}

Regards,
David.

Oh, that .even thing is nice, if that works. I think I must have tried something like that before and it didn’t work. It might only ensure the alignment within the object file, but the object might get loaded into flash starting at an odd address. You should look at the .rst file output by the compiler to see if it really works.

–David

I was hoping to get this working myself, but I am stuck again. I’d be grateful if you would check my DMA channel setup code:


// RAM buffer for the data to be saved.
uint8  XDATA ram_config[1024];

// A pointer to the beginning of page 29 of FLASH memory.
uint8* XDATA flash_config = (uint8*)0x7400;

...

void main() {
	int i = 0;
	uint8 x = 0;

	systemInit();
	usbInit();

	// Put a test pattern in the RAM buffer
	for (i = 0; i < sizeof(ram_config); i++) {
		ram_config[i] = x++;
	}

	dmaConfig._2.SRCADDRH = (unsigned int)ram_config >> 8;
	dmaConfig._2.SRCADDRL = (unsigned int)ram_config & 0x00FF;
	dmaConfig._2.DESTADDRH = (unsigned int)FWDATA >> 8;
	dmaConfig._2.DESTADDRL = (unsigned int)FWDATA & 0x00FF;
	dmaConfig._2.VLEN_LENH = 4; // 000 00100 - fixed length, transfer 100 0000 0000 (0x400)
	dmaConfig._2.LENL = 0x00; // bytes.
	dmaConfig._2.DC6 = 18; // Triggered on FLASH
	dmaConfig._2.DC7 = 74; // 01:00:1:0:10  src inc, no dst inc, use irq, m8 = 0, high pri

	FADDRL = 0;
	FADDRH = 58; // page 29 shifted left by 1 bit

	DMAARM |= 4; // arm DMA channel 2

	// now trigger the DMA by setting FCTL.WRITE.
	triggerDMA();
	// Or try this, same effect.
	//__asm lcall _startFlashWrite __endasm;

... later, after connecting with TeraTerm

				sprintf(response, "flash_config: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X%s",
						flash_config[0], flash_config[1], flash_config[2], flash_config[3], flash_config[4],
						flash_config[5], flash_config[6], flash_config[7], flash_config[8], flash_config[9],
						EOL);

				write_response_to_usb();

For reference, here is the function which tries to trigger the DMA transfer:

                           1687 ;------------------------------------------------------------
                           1688 ;Allocation info for local variables in function 'triggerDMA'
                           1689 ;------------------------------------------------------------
                    0380   1690 	G$triggerDMA$0$0 ==.
                    0380   1691 	C$air_rail_loco.c$175$1$1 ==.
                           1692 ;	apps/air_rail_loco/air_rail_loco.c:175: void triggerDMA() {
                           1693 ;	-----------------------------------------
                           1694 ;	 function triggerDMA
                           1695 ;	-----------------------------------------
   08DA                    1696 _triggerDMA:
                    0380   1697 	C$air_rail_loco.c$181$1$1 ==.
                           1698 ;	apps/air_rail_loco/air_rail_loco.c:181: __endasm;
                           1699 	
                           1700 	
   08DA                    1701 	 .even
   08DA 43 AE 02           1702 	 ORL _FCTL, #0x02;
                           1703 	
                           1704 	 
                    0383   1705 	C$air_rail_loco.c$182$1$1 ==.
                    0383   1706 	XG$triggerDMA$0$0 ==.
   08DD 22                 1707 	ret

The program doesn’t crash, but my pattern of 01 02 03… doesn’t make it into flash. Given it doesn’t work with either triggering function I’m assuming my DMA channel setup is incorrect. Pointer manipulation isn’t my strong point any more. The string to the USB port is sent during the same run, so the flash hasn’t been erased by a program load or anything.

Any ideas?

Regards,
David.

For flash_config, you accidentally made a generic pointer that happens to be stored in XDATA. To make a pointer to XDATA, you should do this:

uint8 XDATA * flash_config = (uint8 XDATA *)0x7400;

SDCC’s generic pointers should be avoided when possible because you can already point to almost anything using a pointer to XDATA.

Your lines that set DESTADDRH and DESTADDRL are missing the & operator so they would not actually take an address, but read a value from FWDATA. However, it’s not so simple to get an XDATA address of an SFR, so you should do this instead:

dmaConfig._2.DESTADDRH = XDATA_SFR_ADDRESS(FWDATA) >> 8;
dmaConfig._2.DESTADDRL = XDATA_SFR_ADDRESS(FWDATA);

Section 12.3.5 tells us to set FWT to a value that depending on the clock frequency. For the Wixel it should be:

FWT = 32;

It would make me comfortable if you use MOV instead of ORL on FCTL, because then you have a better guarantee about what the value of FCTL will be, but that shouldn’t be a problem at this point.

Also, please note that since you are using _2, your code might stop working when we assign DMA channel 2 to some other purpose in the future.

–David

Wow, thanks David. Very comprehensive.

That wasn’t an accident, it was a misunderstanding. I don’t understand the 8051 well enough to know there are different pointer types. I thought the XDATA directive was used to say where to store the variable being declared, not that it specifies what type of memory is being pointed to.

Thanks, I’d never have figured that out.

Thanks.

Is there a more generic form of code which will use a free DMA channel, or should I use channel 4 because Pololu will use them from 1 up, or is this just a warning that my code might need to change in future?

Thanks for your help,
David.

It’s just a warning that your code might need to change in the future. The next DMA channel we assign to something will probably be 2, so your code would be safer if you used _4 or if you set yourself up to use DMA channel 0. However, I recommend getting your existing code to work before you worry about that.

–David

It worked!

I used the function in FLASH with the .even directive to trigger it. Still not sure if the directive works as looking at the listing I think the instruction naturally ended up on an even boundary. I’ll worry about that later.

Thanks for your help!

I’ll change over to DMA 4 and later implement some round-robin set of pages to write to so the FLASH doesn’t die too quickly.

Regards,
David.

Great! I am glad you got it working. --David

Hi there,

I’m new to wixel programming, and it’s my first time programming in C :frowning:

I face the same problem, e.g. I need variables store permanently that I can change within my code. Would it be possible to post the complete working code to do that? I find it very hard to get working code out of this conversation. Sorry to be a numpty.

Best regards,

Christian

Hello, Christian.

I recommend reading section 12.3 of the CC2511F32 datasheet, which describes how to write to flash memory. Also, there is another thread that has some more complete code examples that you might find useful:

–David