C functions for reading and writing XMega Flash memory

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

I read all the application data about self programming, I got the sp_driver.s, but I'm still wondering: any chance someone has the C code for programming the XMega?
Why there is an application (the AVR106) for the tinyAVR and the megaAVR and nothing for the XMega and I have to use the asm code? Plus, the asm in the application is for the IAR or the GCC compiler and I'm not using those compilers so I should port it for the imagecraft compiler.
I don't want to reinvent the wheel, if someone knows where i can find a C driver please point me to it, thanks.

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

Hi effemmeffe.

I too am working with xmega and flash memory. I have not managed to get anything working. But I can help you out - AVR1316: XMEGA Self-programming has the sp_driver.s and sp_driver.h. The header file has the C functions for calling the asm functions. It also contains a 'working' example. You can find it on the atmel website - find your device and the documentation. There seems to be a few things to watch out for with the sp_driver,

1) You must consider CCP
2) it does not clean up the NVM Command Reg automatically
3) requires a linker command to create a boot section at 0x20000. I am not sure what happens when you use the -Ttext=0x20000 in the bootloader case...

If anyone knows how to make the sp_driver work for writing to flash, your suggestions would be great.

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

I trust you have both seen Atmel Application Note: AVR1612: PDI Programming Driver.

It contains info which is helpful for both writing an External PDI programmer and for writing an Internal Boot Loader.

JC

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

You don't mention which XMega you are using and, since you are using Atmel code, there may be a workaround in the code but, for the XMega-A1 at least, the errata in the datasheet says:

Quote:
29. Some NVM Commands are non-functional
The following NVM commands are non-functional:

– 0x2B Erase Flash Page
– 0x2E Write Flash Page
– 0x2F Erase & Write Flash Page
– 0x3A Flash Range CRC

Problem fix/Workaround
None for Flash Range CRC
Use separate programming commands for accessing application and boot section.

– 0x22 Erase Application Section Page
– 0x24 Write Application Section Page
– 0x25 Erase & Write Application Section Page
– 0x2A Erase Boot Loader Section Page
– 0x2C Write Boot Loader Section Page
– 0x2D Erase & Write Boot Loader Section Page

Gamu The Killer Narwhal
Portland, OR, US
_________________
Atmel Studio 6.2
Windows 8.1 Pro
Xplained boards mostly

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

Thanks for the replies. I am using the atxmega128d4. It has no errata which atmel has confirmed to me i.e. they have fixed the 32/64d4 errata.

I have managed to verify that the SP_EraseApplicationSection command might be working - I can use my bootload and application code - both downloaded using PDI and it switches between them find. If the bootloader calls SP_EraseApplicationSection the application code no longer runs.. so it did something!

Next I want to verify if it has been erased properly i.e. read back 0xFF. Can someone guide me on how to do this? I have tried for weeks with no joy. My current attempt is, usartTxByte(SP_ReadWord(0));

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

Code memory is "PROGMEM". You can read the entire app space with:

#include 

...

for (i=0; i
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just wanted to state what my problem was,

when using the stk600 you must remove the PDI/ISP connector to write to the flash. Others have reported that generally all external programmers should be removed when self programming.

All the best.

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

I decided to write my own flash driver, using the SP_driver.h as inspiration for the C prototypes.
I just wrote the functions I need to erase the page buffer, fill it and then write it on the flash and to read it back.
I was wondering why Atmel do provide all the drivers in plain C and this one in asm. Then I wrote it myself and I discovered that some instruction can't be write in C, like "spm". But I prefer to have some asm("spm") instruction in my code than to have all the driver in assembler.

I have a boot that can receive a full application via the USART and write it on the application section and then jump on it.
Once I'll finish the testing I'd like to share my effort somehow, maybe publishing the code.

Right now I'm struggling with the boot and the application startups and how to manage the noinit zone in the RAM, but this is another issue.

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

I enclose here the code I wrote.
It's working on my XMega256A3.
Comments may be outdated and the code is not tested other than in my application.
YMMV

---flash_driver.h---

// XMega 256A3
#define FLASH_PAGE_SIZE                     256
#define FLASH_FWORD_SIZE                    9
#define FRAZIONI_DI_PAGINA_FLASH            4




/*! \brief Non-Volatile Memory Execute Command
 *
 *  This macro set the CCP register before setting the CMDEX bit in the
 *  NVM.CTRLA register.
 *
 *  \note The CMDEX bit must be set within 4 clock cycles after setting the
 *        protection byte in the CCP register.
 */
#ifndef NVM_EXEC
#define NVM_EXEC()  asm("push r30"      "\n\t"  \
                        "push r31"      "\n\t"  \
                        "push r16"      "\n\t"  \
                        "push r18"      "\n\t"  \
                        "ldi r30, 0xCB" "\n\t"  \
                        "ldi r31, 0x01" "\n\t"  \
                        "ldi r16, 0xD8" "\n\t"  \
                        "ldi r18, 0x01" "\n\t"  \
                        "out 0x34, r16" "\n\t"  \
                        "st Z, r18"     "\n\t"  \
                        "pop r18"       "\n\t"  \
                        "pop r16"       "\n\t"  \
                        "pop r31"       "\n\t"  \
                        "pop r30"       "\n\t"  \
                       )
#endif // NVM_EXEC



uint8_t FLASH_ReadByte(uint32_t);
void FLASH_FlushFlasPageBuffer(void);
void FLASH_LoadFlashPageBuffer(const uint8_t *);
void FLASH_EraseApplicationSections(void);
void FLASH_EraseWriteApplicationPage(uint16_t);
uint32_t FLASH_ApplicationCRC(void);
uint32_t FLASH_RangeCRC(uint32_t, uint32_t);
void FLASH_WaitForNVM(void);
void FLASH_ReadFlashPage(uint8_t *, uint32_t);

---flash_driver.h---



---flash_driver.c---

/*! \brief Flush temporary FLASH page buffer.
 *
 *  This function flushes the FLASH page buffers.
 */
void FLASH_FlushFlasPageBuffer(void)
{
    NVM.CMD = NVM_CMD_ERASE_FLASH_BUFFER_gc;
    NVM_EXEC();

    // Wait until NVM is not busy
    FLASH_WaitForNVM();
}   // FLASH_FlushFlasPageBuffer



/*! \brief Load entire page into temporary FLASH page buffer.
 *
 *  This function loads an entire FLASH page from an SRAM buffer to
 *  the FLASH page buffers. Make sure that the buffer is flushed before
 *  starting to load bytes.
 *
 *  \note Only the lower part of the address is used to address the buffer.
 *        Therefore, no address parameter is needed. In the end, the data
 *        is written to the FLASH page given by the address parameter to the
 *        FLASH write page operation.
 *
 *  \param  values   Pointer to SRAM buffer containing an entire page.
 */
void FLASH_LoadFlashPageBuffer(const uint8_t *ram_buffer_ptr)
{
    uint16_t i;
    uint16_t *int_ptr;

    // Wait until NVM is not busy
    FLASH_WaitForNVM();
    NVM.CMD = NVM_CMD_LOAD_FLASH_BUFFER_gc;

    int_ptr = (uint16_t *)ram_buffer_ptr;

    // Load multiple bytes into page buffer
    for (i = 0; i < (FLASH_PAGE_SIZE * 2); i+=2)
    {
        LoadR0(*int_ptr);
        LoadZ(i);
        CPU.CCP = CCP_SPM_gc;
        asm("spm");
        int_ptr++;
    }
}   // FLASH_LoadFlashPageBuffer



/*! \brief Erase entire application section.
 *
 *  This function erases the entire application and application table section
 *
 *  \note If the lock bits is set to not allow spm in the application or
 *        application table section the erase is not done.
 */
void FLASH_EraseApplicationSections(void)
{
    NVM.CMD = NVM_CMD_ERASE_APP_gc;
    CPU.CCP = CCP_SPM_gc;
    // Self-program.
    asm("spm");

    // Wait until NVM is not busy
    FLASH_WaitForNVM();
}   // FLASH_EraseApplicationSections



/*! \brief Erase and write page buffer to application or application table section at byte address.
 *
 *  This function does a combined erase and write to a flash page in the application
 *  or application table section.
 *
 *  \param page_number Flash page number.
 */
void FLASH_EraseWriteApplicationPage(uint16_t page_number)
{
    // addresses the page
    CPU.RAMPZ = (uint8_t)(page_number >> (16 - FLASH_FWORD_SIZE));
    LoadZ((uint32_t)page_number << FLASH_FWORD_SIZE);

    // Write the "safety code" to the CCP regiter
    // FLASH write has to be executed within 4 cycles
    NVM.CMD = NVM_CMD_ERASE_WRITE_APP_PAGE_gc;
    CPU.CCP = CCP_SPM_gc;
    // Self-program.
    asm("spm");

    // Wait until NVM is not busy
    FLASH_WaitForNVM();
}   // FLASH_EraseWriteApplicationPage



/*! \brief Generate CRC from a flash range.
 *
 *  \retval 24-bit CRC value
 */
uint32_t FLASH_RangeCRC(uint32_t start_address, uint32_t end_address)
{
    uint32_t RetVal;

    // Wait until NVM is not busy
    FLASH_WaitForNVM();


    // load start address
    NVM.ADDR2 = (uint8_t)(start_address >> 16);
    NVM.ADDR1 = (uint8_t)(start_address >> 8);
    NVM.ADDR0 = (uint8_t)start_address;

    // load end address
    NVM.DATA2 = (uint8_t)(end_address >> 16);
    NVM.DATA1 = (uint8_t)(end_address >> 8);
    NVM.DATA0 = (uint8_t)end_address;

    // load and execute NVM command
    NVM.CMD = NVM_CMD_FLASH_RANGE_CRC_gc;
    NVM_EXEC();

    // read the return value
    RetVal = NVM.DATA2;
    RetVal = RetVal << 8;
    RetVal |= NVM.DATA1;
    RetVal = RetVal << 8;
    RetVal |= NVM.DATA0;

    return RetVal;
}   // FLASH_RangeCRC



/*! \brief Generate CRC from application section.
 *
 *  \retval 24-bit CRC value
 */
uint32_t FLASH_ApplicationCRC(void)
{
    uint32_t RetVal;

    // Wait until NVM is not busy
    FLASH_WaitForNVM();

    // load and execute NVM command
    NVM.CMD = NVM_CMD_APP_CRC_gc;
    NVM_EXEC();

    // read the return value
    RetVal = NVM.DATA2;
    RetVal = RetVal << 8;
    RetVal |= NVM.DATA1;
    RetVal = RetVal << 8;
    RetVal |= NVM.DATA0;

    return RetVal;
}   // FLASH_ApplicationCRC



/*! \brief Read a byte from flash.
 *
 *  This function reads one byte from the flash.
 *
 *  \note Both IAR and GCC have functions to do this, but
 *        we include the fucntions for easier use.
 *
 *  \param address Address to the location of the byte to read.
 *
 *  \retval Byte read from flash.
 */
uint8_t FLASH_ReadByte(uint32_t flash_address)
{
    uint8_t flash_data;

    CPU.RAMPZ = (uint8_t)(page_address >> 16);
    LoadZ(flash_address);
    flash_data = ReadELPM();

    return flash_data;
}   // FLASH_ReadByte




/*! \brief Wait for any NVM access to finish, including FLASH.
 *
 *  This function is blcoking and waits for any NVM access to finish,
 *  including FLASH. Use this function before any FLSH accesses,
 *  if you are not certain that any previous operations are finished yet,
 *  like an FLASH write.
 */
void FLASH_WaitForNVM(void)
{
    do {
        /* Block execution while waiting for the NVM to be ready. */
    } while ((NVM.STATUS & NVM_NVMBUSY_bm) == NVM_NVMBUSY_bm);
}   // FLASH_WaitForNVM




/*! \brief Read entire Flash page into SRAM buffer.
 *
 *  This function reads an entire flash page and puts it to SRAM.
 *
 *  \param data        Pointer to where to store the data.
 *  \param page_number Flash page number.
 */
void FLASH_ReadFlashPage(uint8_t *ram_buffer, uint32_t page_number)
{
    uint16_t idx;
    uint32_t base_address;

    NVM.CMD = NVM_CMD_NO_OPERATION_gc;

    CPU.RAMPZ = (uint8_t)(page_number >> (16 - FLASH_FWORD_SIZE));

    base_address = page_number << FLASH_FWORD_SIZE;

    for (idx = 0; idx < (FLASH_PAGE_SIZE * 2); idx++)
    {
        LoadZ((uint32_t)(base_address + idx));
        *ram_buffer = ReadELPM();
        ram_buffer++;
    }
}   // FLASH_ReadFlashPage

---flash_driver.c---





---flash_example.c---

#define START_INTERNAL_CRC                  0x3FFF5

uint8_t buffer_flash_page[FLASH_PAGE_SIZE * 2];


// this function calculate the CRC of almost all the flash memory and then stores the CRC in the flash itself
// (the reason why the CRC isn't calculated on all the flash memory is because the storing of the CRC would modify the CRC itself for the next time,
// so the CRC is calculated for all the flash but the location in which the CRC will be stored)
void Store_Application_CRC(void)
{
    uint32_t application_internal_CRC;
    uint16_t application_external_CRC;
    uint8_t flash_data;

    // CRC calculation of almost all Flash memory
    application_internal_CRC = FLASH_RangeCRC((uint32_t)0, (uint32_t)(START_INTERNAL_CRC - 1));

    // load last flash page in RAM
    FLASH_ReadFlashPage(buffer_flash_page, (uint32_t)((FLASH_PAGE_SIZE * 2) - 1));

    // write the calculated CRC in the RAM buffer
    buffer_flash_page[START_INTERNAL_CRC % 512] = (uint8_t)(application_internal_CRC >> 16);
    buffer_flash_page[(START_INTERNAL_CRC % 512) + 1] = (uint8_t)(application_internal_CRC >> 8);
    buffer_flash_page[(START_INTERNAL_CRC % 512) + 2] = (uint8_t)application_internal_CRC;

    // empty page buffer
    FLASH_FlushFlasPageBuffer();

    // load the RAM buffer in the page buffer
    FLASH_LoadFlashPageBuffer(buffer_flash_page);

    // erase & write of the flash page
    FLASH_EraseWriteApplicationPage((uint32_t)((FLASH_PAGE_SIZE * 2) - 1));
}   // Store_Application_CRC


---flash_example.c---
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi effemmeffe

effemmeffe wrote:
I enclose here the code I wrote.

really great thanks to you.
I'm working for this for lot of day but not getting right good and at last, i found your code.

Well when i paste this to my complier i got these errors as

Error: Program.c(991): undefined symbol 'LoadR0'
Error: Program.c(1010): undefined symbol 'CPU'
Error: Program.c(1030): undefined symbol 'CPU'
Error: Program.c(1031): undefined symbol 'LoadZ'
Error: Program.c(1036): undefined symbol 'CPU'
Error: Program.c(1126): undefined symbol 'CPU'
Error: Program.c(1127): undefined symbol 'LoadZ'
Error: Program.c(1167): undefined symbol 'CPU'
Error: Program.c(1173): undefined symbol 'LoadZ'

where is the function defined for all this.. do i want to include any header file or c file for this purpose?

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

I forgot to post the assembly file in which you can find the assembly routines for loading the registers Z and R0.
Keep in mind it's wrote for the Imagecraft compiler, you have to modify it for other compilers.
And about the heder file to include: I use the Imagecraft's include files, CPU is defined in it.
For example, CPU.RAMPZ is the I/O 0x3B for the XMega256A3.
HTH HAND

-----Utility.s-----

_LoadZ::
  movw  Z, r16    ; Load R17:R16 into Z.
  ret



_LoadR0::
  movw  r0, r16   ; Load R17:R16 into R1:R0.
  ret



_ReadELPM::
  elpm r16, Z
  ret
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hi,
I'm dealing with a ATXMEGA256A3U micro, and trying to write a flash page operation.

j used AVR1008 application note with sp_driver.s and sp_driver.h for gcc.

j tried to write a buffer (from 0 to 511)on a flash page , but when j read again the page j don't read it

This is my code:

#define FLASH_PAGE_SIZE 256
uint8_t buffer_flash_page[FLASH_PAGE_SIZE * 2];

void example(void)
{

// load last flash page in RAM
SP_ReadFlashPage(buffer_flash_page, (uint32_t)((FLASH_PAGE_SIZE * 2) - 1));

for (uint16_t i = 0 ; i <512 ; i++)
buffer_flash_page[i] = (uint8_t) i;

// empty page buffer
ClearFlashBuffer();;

// load the RAM buffer in the page buffer
SP_LoadFlashPage(buffer_flash_page);

// erase & write of the flash page
EraseWriteApplicationPage((uint32_t)((FLASH_PAGE_SIZE * 2) - 1));

// load last flash page in RAM
SP_ReadFlashPage(buffer_flash_page, (uint32_t)((FLASH_PAGE_SIZE * 2) - 1));
}

when j read again buffer_flash_page there aren't number from 0 to 511.
If anyone is able to help me, I would be very much grateful.

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

The code that writes pages must be in the bootloader section. That makes updating the bootloader itself a bit tricky.

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

fflavoro please see this http://www.avrfreaks.net/index.p...

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#define FLASH_PAGE_SIZE 256 
uint8_t buffer_flash_page[FLASH_PAGE_SIZE * 2]; 

void example(void) 
{ 


// load last flash page in RAM 
SP_ReadFlashPage(buffer_flash_page, (uint32_t)((FLASH_PAGE_SIZE * 2) - 1)); 


for (uint16_t i = 0 ; i <512 ; i++) 
buffer_flash_page[i] = (uint8_t) i; 


// empty page buffer 
ClearFlashBuffer();; 

// load the RAM buffer in the page buffer 
SP_LoadFlashPage(buffer_flash_page); 

// erase & write of the flash page 
EraseWriteApplicationPage((uint32_t)((FLASH_PAGE_SIZE * 2) - 1)); 



// load last flash page in RAM 
SP_ReadFlashPage(buffer_flash_page, (uint32_t)((FLASH_PAGE_SIZE * 2) - 1)); 
} 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

when j read again buffer_flash_page there aren't number from 0 to 511.
If anyone is able to help me, I would be very much gratefu

What do you get back?

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

Hi Effemeffe,
I ran the above code that you have attached and I get a error message "page_address" undeclared first use in the function. The page_address variable is used in the FLASH_readbyte function. Please help. Thanks.

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

I'm guessing page_address was supposed to be flash_address (the function parameter) but if so this suggests the code was posted untested. That's the problem with code from the internet - you cannot determine the provenance.