| Author |
Message |
|
|
Posted: Nov 09, 2007 - 12:53 PM |
|

Joined: Oct 08, 2007
Posts: 23
|
|
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. |
|
|
| |
|
|
|
|
|
Posted: Nov 09, 2007 - 12:59 PM |
|


Joined: Jul 18, 2005
Posts: 33138
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
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/user-man ... _boot.html |
_________________
|
| |
|
|
|
|
|
Posted: Nov 09, 2007 - 01:26 PM |
|

Joined: Dec 08, 2004
Posts: 4493
Location: Nova Scotia, Canada
|
|
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 <avr/boot.h> 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. |
|
|
| |
|
|
|
|
|
Posted: Nov 09, 2007 - 01:42 PM |
|


Joined: Jul 18, 2005
Posts: 33138
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
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 |
_________________
|
| |
|
|
|
|
|
Posted: Nov 09, 2007 - 04:24 PM |
|

Joined: Oct 08, 2007
Posts: 23
|
|
Well, first of all thank you for your quick replies!
I tried to do a simple program to write the flash, here it is.
Code:
#include <avr/io.h>
#include <avr/boot.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
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<SPM_PAGESIZE; i+=2)
{
// 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.
// Reenable RWW-section again. We need this if we want to jump back
// to the application after bootloading.
boot_rww_enable ();
// Re-enable interrupts (if they were ever enabled).
sei();
SREG = sreg;
}
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. |
|
|
| |
|
|
|
|
|
Posted: Nov 09, 2007 - 04:45 PM |
|


Joined: Jul 18, 2005
Posts: 33138
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
For my mega16 app my Makefile includes the following additions:
Code:
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 by clawson on Nov 09, 2007 - 04:47 PM; edited 1 time in total
|
| |
|
|
|
|
|
Posted: Nov 09, 2007 - 04:47 PM |
|

Joined: Dec 08, 2004
Posts: 4493
Location: Nova Scotia, Canada
|
|
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:
Code:
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. |
|
|
| |
|
|
|
|
|
Posted: Nov 12, 2007 - 06:28 PM |
|

Joined: Oct 08, 2007
Posts: 23
|
|
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. |
|
|
| |
|
|
|
|
|
Posted: Dec 10, 2009 - 12:47 PM |
|

Joined: May 09, 2009
Posts: 15
|
|
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<SPM_PAGESIZE; i+=2)
{
// 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.. |
|
|
| |
|
|
|
|
|