Write FLASH memory on ATmega128

Go To Last Post
31 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi everyone, I would like to know if it is possible to write a byte or a word in the flash memory during the execution of the program. I basically need this to update the values of a char array, whose size is too big to be put in the EEPROM memory. Is there something to achieve that?

Thanks in advance.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Look at "Bootloader Support" section of the 128 datasheet. To be able to write back to it's own flash involves the use of the SPM opcode but on a 128 you can only execute this from within a special section of the flash (up near the end) called the Boot Loader Section. By default it is located at byte address 0x1E000.

As you posted in GCC I guess it's a fair assumption that's what you are using in which case you are going to find avr/boot.h very useful as it contains the SPM routines you need and is documented at:

http://www.nongnu.org/avr-libc/u...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is possible. Read the section of the datasheet entitled "Boot Loader Support" for the low-level details. Also check out the avr-libc manual dealing with the header for a description of some macros you can use to make it easier to code in GCC.

Note that these two documents are written from the perspective that you are going to be writing a complete upgrade for your application firmware using this mechanism. However it can also be applied, with caution, to the general case of modifying only certain portions of data that happen to be contained in Flash.

Remember, though, that it isn't possible to change just 1 single byte or word at a time. The Flash usually has to be erased before it can be re-written, and you can only erase Flash in groups called "pages". In an ATmega128, each page is 256 bytes long. So your Flash writing routine may have to be able to detect situations in which it is only being provided with data to replace a portion of a page, while the rest of the page should retain its previous value. In those cases it needs to read the previous content of the Flash page into a temporary buffer so that the unmodified portion can be restored after the page has been erased.

It'll also make life easier if you locate your char array at a fixed location in Flash, with its beginning and end aligned to 256-byte page boundaries. That way you reduce the risk that modifying that array might temporarily leave other unrelated data or (worse) executable code in an invalid state during the time that the Flash write is in progress.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh and since neither of us mentioned it can I just remind you that any particular page in the 128's code flash can only be erased 10,000 times during the entire life of the chip (unlike the EEPROM which can stand 100,000 cycles and is byte accessible)

BTW, as you are going to be combining "boot" code within the application code I'd suggest you are going to find BOOTLOADER_SECTION defined in avr/boot.h particularly useful. Remember that ALL the routines that must remain visisble during the code writing process must be declared BOOTLOADER_SECTION as the main app space will not be visible during the writes (and you may want to think about the impact of interrupts and an "invisible" interrupt vector table as well)

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, first of all thank you for your quick replies!
I tried to do a simple program to write the flash, here it is.

#include 
#include 
#include 
#include 
#include 

void boot_program_page (uint32_t page, uint8_t *buf) BOOTLOADER_SECTION;

uint8_t c[256] = "0123456789";

int main (void) 
{ 
	
	uint32_t i=0;
	
	for(i = 10; i < 256; i++)
		c[i] = 0x00;

	boot_program_page(0x100, c);

	return (0);
} 


void boot_program_page (uint32_t page, uint8_t *buf)
{
	uint16_t i;
    uint8_t sreg;

    // Disable interrupts.

    sreg = SREG;
    cli();
    
    eeprom_busy_wait ();

    boot_page_erase (page);
    boot_spm_busy_wait ();      // Wait until the memory is erased.

    for (i=0; i

I didn't succeeded in compiling it, because I get the message:

c:\winavr-20070525\bin\..\lib\gcc\avr\4.1.2\..\..\..\..\avr\bin\ld.exe: section .data [0000016e -> 0000026d] overlaps section .bootloader [0000016e -> 0000029f]

I have no idea of what it means and I really don't know how to fix it.
Furthermore I would like to know how to handle the 'page' parameter in the boot_program_page function, I mean...can I choose an arbitrary address or has it to be a multiple of SPM_PAGESIZE?
Again, thank you for your help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For my mega16 app my Makefile includes the following additions:

BOOTLOAD = 0x3800

LDFLAGS += -Wl,--section-start=.bootloader=$(BOOTLOAD) 

in the "Linker options" section.

When you declared boot_program_page to be BOOTLOADER_SECTION above all you did was move the code of the function out of the normal .text section and into a named section called .bootloader. But now you also need a command to the linker to say where you'd like .bootloader to be placed in code space

Cliff

PS assuming you haven't changed the default state of the BOOTSZ fuses in your '128 then BOOTLOAD = 0x1E000 in your equivalent of the above

Last Edited: Fri. Nov 9, 2007 - 04:47 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The BOOTLOADER_SECTION tag that you add to your function prototype instructs the compiler to make the function a member of a special section in memory called ".bootloader". You still need to modify the instructions given to the linker to instruct it where in Flash to locate that section. By default, the linker will attempt to place it at the same address as the standard Flash .data section, which obviously cannot work.

Basically, you need to add a line to your makefile, in the section defining LDFLAGS, that looks like this:

LDFLAGS += -Wl,--section-start=.bootloader=0xXXXXXXXX

where 0xXXXXXXXX is a byte-oriented address somewhere in the bootloader section of the ATmega128. (The bootloader section's size may vary depending on your fuse bits.)

As for the 'page' parammeter:
Remember that the function as it exists in your example assumes that you'll always provide a complete page's worth of data (256 bytes) in a single pass. If you want to be able to modify smaller (or larger) chunks, then you'll have to either rewrite the function or wrap it inside a higher level driving function.

And yes, that function as it exists in your example requires that the starting address passed through 'page' must always be at the beginning of a page boundary, otherwise there will be wrap-around problems.

Also remember that you'll need some means of guaranteeing that an adequate portion of Flash is always set aside to accommodate your char array, so that you don't unwittingly overwrite executable code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You guys have been really great! Everything works fine. I have got a last question which follows from your hints...is it possible to force vector (defined with the PROGMEM attribute) to be placed at the desired flash location (of course I would like it to be placed at the beginning of a page)?
Thanks a lot.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am facing a strange problem.I am writing a function named prgram_page which is used to write data into flash memory.I am writing this function in bootloader section (at the location 0xFC00).

In main I am calling 'program_page(add,arr);'
But the problem is that the string 'arr' is not getting written at the location 'add' as in code below.But instead it is getting written below the program_page definition in bootloader section.

The code I am using is below:-

int main()
{
uint8_t arr[]={1,2,3,4,5,8,0,0,7,5,5,5,5,5};
uint32_t add=0x0930;
program_page(add,arr);
return 0;
}

asm(".org 0xFC00");
void program_page (uint32_t page, uint8_t *buf)
{
uint16_t i;
uint8_t sreg;

// Disable interrupts.

sreg = SREG;
cli();

eeprom_busy_wait ();

boot_page_erase(page);
boot_spm_busy_wait (); // Wait until the memory is erased.

for (i=0; i {
// Set up little-endian word.

uint16_t w = *buf++;
w += (*buf++) << 8;

boot_page_fill(page + i, w);
}

boot_page_write (page); // Store buffer in flash page.
boot_spm_busy_wait(); // Wait until the memory is written.

boot_rww_enable ();

// Re-enable interrupts (if they were ever enabled).

SREG = sreg;
}//endcode

I want to write data at a desired location.Please help me out.Thanks in advance..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Jam82 and everyone,
Could you explain your source above to me? It start with an aplication and jump to bootloader sector and then write some data in the page of the flash, it is right? Could you detail it to me? Thanks very much!

Abraão

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No, the code you are looking at is located at the BLS. What the OP is doing is writing "0123456789" followed by 245 0x00's to flash address 0x0000 then he exits main(). This is never a great idea in an embedded program but modern versions of GCC catch this silly error and just loop in a tight, no interrupt loop so no damage is done.

I guess the OP's intention was to run this then to use his ISP programmer to read out from flash at location 0x0000 and see if he can spot the "0123456789" that's been written there.

If you thought:

return(0);

would do a jump to 0x0000 you are quite wrong (and what would one hope to happen if the AVR executed the opcodes represented by "0123456789" anyway?). If he really had programmed code to 0x0000 and wanted to jump to it the standard C way to achieve that is:

typedef void (*f_ptr_type)(void);

f_ptr_type call_zero = (f_ptr_type) 0x0000;

...

call_zero();

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I understood!
The function:

boot_program_page(0x100, c);

What is the meaning of 0x100?
The modern version of GCC when the program found:

return(0);

The program execute again the:

int main(){ (...) }

It´s right?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

What is the meaning of 0x100?

Look at the code he presented above:

void boot_program_page (uint32_t page, uint8_t *buf) 
{ 

so the 0x100 is the page number. As the 128 has 256 bytes pages this is address 65536 (64K)

Quote:

The program execute again the:

No. it's easy to see what this does. Compile the program:

#include  

int main(void) 
{ 
   return 0; 
} 

then study the .lss file:

00000068 <__ctors_end>:
  68:	11 24       	eor	r1, r1
  6a:	1f be       	out	0x3f, r1	; 63
  6c:	cf ef       	ldi	r28, 0xFF	; 255
  6e:	d8 e0       	ldi	r29, 0x08	; 8
  70:	de bf       	out	0x3e, r29	; 62
  72:	cd bf       	out	0x3d, r28	; 61
  74:	0e 94 40 00 	call	0x80	; 0x80 
78: 0c 94 43 00 jmp 0x86 ; 0x86 <_exit> 0000007c <__bad_interrupt>: 7c: 0c 94 00 00 jmp 0 ; 0x0 <__vectors> 00000080
: #include int main(void) { return 0; } 80: 80 e0 ldi r24, 0x00 ; 0 82: 90 e0 ldi r25, 0x00 ; 0 84: 08 95 ret 00000086 <_exit>: 86: f8 94 cli 00000088 <__stop_program>: 88: ff cf rjmp .-2 ; 0x88 <__stop_program>

As always the code before main() uses "CALL main" to enter your own code. In this case it loads R24/R25 (the 16 bit 'int' return value) with 0 as a result of the "return 0;". Then it RETs. It will arrive at the instruction after the CALL main where it then does "JMP _exit". At _exit: it does a CLI to disable interrupts then enters _stop_program: which is simply a "RJMP _stop_program" so this then loops until the end of time in a loop that cannot be interrupted (as a result of the CLI).

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jam82 wrote:
You guys have been really great! Everything works fine. I have got a last question which follows from your hints...is it possible to force vector (defined with the PROGMEM attribute) to be placed at the desired flash location (of course I would like it to be placed at the beginning of a page)?
Thanks a lot.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

atelsdevices wrote:
Jam82 wrote:
You guys have been really great! Everything works fine. I have got a last question which follows from your hints...is it possible to force vector (defined with the PROGMEM attribute) to be placed at the desired flash location (of course I would like it to be placed at the beginning of a page)?
Thanks a lot.
is it possible to force a Array or Variable (defined with the PROGMEM attribute) to be placed at the desired flash location (of course I would like it to be placed at any part of a page)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

It is possible to place anything (variable, array, function..) at a fixed location in any memory space (flash, RAM, EEPROM..).

The technique centres around putting the item into a separate named linker section then telling the linker the absolute address you want it positioned at.

Something like

__attribute__((section(".foo"))) uint8_t data[] = { "hello" } ;

then tell the linker:

-Wl, -section-start=.foo=0x2C00

That array of data is now located at 0x2C00 in flash. If you use:

uint8_t byte= pgm_read_byte((uint8_t *) 0x2C01);

it will get 'e' from "hello".

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Gang,

I am attempting to use Jam's code in the post above(5th).

The code compiles and I can set breakpoints with my JTAG. However the Flash does not appear to be changed. Is this a limitation of the JTAG?

Jeff

Jeff Dombach, JLD Systems
"We do the stuff behind the buttons!"
Your source for embedded solutions with a 100% Guarantee.
http://www.jldsystems.com
Phone 717.892.1100

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using an ATMEGA1281V.

In my makefile I set:

BOOTLOAD = 0xFC00

In the main() function I have:

boot_program_page(0x0f0, c);

I am looking at address 0xF000 to see the flash change.

What am I missing?

Jeff Dombach, JLD Systems
"We do the stuff behind the buttons!"
Your source for embedded solutions with a 100% Guarantee.
http://www.jldsystems.com
Phone 717.892.1100

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

BOOTLOAD = 0xFC00

Almost certainly wrong. That looks like you are using a word address where a byte address is required - double it.

Also some JTAG systems (like AS6) cache what they believe the contents of Flash to be and will not show updates. There's a setting somewhere in AS6 you can change for this. Other JTAG debugging systems may operate differently.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay, so I set:

BOOTLOAD = 0x1F800

Using the AS4/JTAGMKII I can see the code at word address 0xFC00.

I have tried 0xF0, 0xF8, 0xC0 for the page.

I still don't see the flash changing values.

Jeff Dombach, JLD Systems
"We do the stuff behind the buttons!"
Your source for embedded solutions with a 100% Guarantee.
http://www.jldsystems.com
Phone 717.892.1100

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jldsystems wrote:
In the main() function I have:

boot_program_page(0x0f0, c);

I am looking at address 0xF000 to see the flash change.

The first parameter of boot_program_page is not a page number, but an address.

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Damn! I must be very numerically challenged today.

Changed to:

boot_program_page(0x01FC00, c);

And now I can see the values change at 0xFE00.

Once again all of you have pulled me kicking and screaming though a mental block!

Thanks and Happy New Year!

Jeff

Jeff Dombach, JLD Systems
"We do the stuff behind the buttons!"
Your source for embedded solutions with a 100% Guarantee.
http://www.jldsystems.com
Phone 717.892.1100

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

my problem is, i would like to write a function which is write data in to the program memory, if i make a bootsection in the makefile and i put this fuction in to the bootmemory with these command:
__attribute__ ((section (".bootloader"))); (in that case the main fuction is in the program memory and it calls my function), it put the fuction in to the bootmemory (i check the lss file) but the fuction unable to write the program memory. I have a atmega128, i use AVR studio 4 with winAVR compiler.

i used the boot.h functions to write the memory. What is wrong with that? How i need to declare the sections?

To declarate the boot section i write the command at the makefile as Clawson said (BOOTSZ=4kb, so boot section start address is 0xF000):
BOOTLOAD = 0xF000
LDFLAGS += -Wl,--section-start=.bootloader=$(BOOTLOAD)

I found something intresting, if i open the lss file, there is a headline like that:

avr_C_sampl.elf: file format elf32-avr

Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000017e 00000000 00000000 00000094 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .bootloader 0000013c 0000f000 0000f000 00000212 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE

the READONLY title looks bad, how can i set this?

thx

Last Edited: Sat. Apr 5, 2014 - 02:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

All code sections in an AVR are READONLY. That's to be expected. Which AVR is this?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
All code sections in an AVR are READONLY. That's to be expected. Which AVR is this?

It is a atmega128, and i use AVR studio 4 with WINavr compiler.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

0x3800 is not a valid BLS address for a 128?!?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
0x3800 is not a valid BLS address for a 128?!?

Sry i set the BOOTSZ bits for 4kb so the boot sections address is 0xF000, i just copy and paste your example here.

PS: i edited my original question,

Last Edited: Sat. Apr 5, 2014 - 03:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
so the boot sections address is 0xF000

In which case you should be setting BOOTLOAD to 0x1E000 (you need to convert Atmel's word address to the byte address GCC wants to use).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:
so the boot sections address is 0xF000

In which case you should be setting BOOTLOAD to 0x1E000 (you need to convert Atmel's word address to the byte address GCC wants to use).

In that case, if i read back the hex file from the atmega, only FFFFF...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,
I am trying to ship new application code to the micro controller and burn it into the Flash. After the entire code has been written into the flash I want to switch to the new application code. How can this be done in AVR?

Regards,
Mihir

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you talking about writing in the sense of a bootloader? if so the "usual" app start is:

typedef void (*fn_ptr_t)(void);

int main(void) {
  fn_ptr_t reset = (fn_ptr_t)0x00000;
  program_flash();
  reset();
}

That just defines a function pointer called "reset", assigns the value 0x0000 (start of app code) to it and then calls through the pointer. Don't worry that it's a call not a jump because the first thing the app code will do is reset the stack pointer so the fact that a return address is stored there will be lost.

In reality the bootloader is probably a little more complex than this. If it has used some of the AVR peripherals it either needs to put everything back to the power on state manually or, more usually, it enables the watchdog then just loops until the AVR resets. Because of BOOTRST control returns to the start of the bootloader only there it will have code to recognise that the reason for the reset was the watchdog (WDRF is set) and it is there that reset will be set to 0 and reset() called.