AVR XMEGA Writing to internal Flash in GCC (Bootloader)

1 post / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

I have written a Self Programming driver for ARV GCC, and while doing so I noticed there is not much out there in the way of programming the SPM for this compiler, so thought i might share mine.  It is heavily inspired by AVR1316 application note / sp_driver.S so it is mostly inline assembly (most of the comments are a direct copy).

 

Also keep in mind these functions need to be executed from the bootloader section of flash memory, your linker needs the following flag (xmega256a3u is at the byte address of 0x40000): 

-Wl,--section-start=.bootloader=0x40000

Unless your .text section starts at your bootloader address, then you don't need any of the BOOTLOADER_SECTION definitions below.

 

Hopefully this saves a bit of head scratching ...

 

 

-------------------- bootloader.h ------------------------------------

#ifndef BOOTLOADER_H_

#define BOOTLOADER_H_

 

#define BOOTLOADER_SECTION   __attribute__ ((section (".bootloader")))

 

void SP_WaitForSPM() BOOTLOADER_SECTION;

void flash_write_page (uint32_t addr, uint8_t *buf) BOOTLOADER_SECTION;

void flash_erase_page(uint32_t addr) BOOTLOADER_SECTION;

void flash_read_page (uint32_t addr, uint8_t *buf) BOOTLOADER_SECTION;

 

#endif /* BOOTLOADER_H_ */

 

 

-------------------- bootloader.c ------------------------------------

 

#include <avr/io.h>

#include <avr/interrupt.h>

#include <avr/pgmspace.h>

 

#include "bootloader.h"

 

#define ADDR_RAMPX       0x0039

#define ADDR_RAMPY       0x003A

#define ADDR_RAMPZ       0x003B

#define ADDR_NVM_CMD     0x01CA

#define ADDR_CCP         0x0034

#define ADDR_NVM_CTRLA   0x01CB

#define ADDR_NVM_DATA0   0x01C4

#define ADDR_NVM_DATA1   0x01C5

#define ADDR_NVM_DATA2   0x01C6

 

void SP_WaitForSPM() {

  while (NVM.STATUS & NVM_NVMBUSY_bm);

  NVM_CMD = 0;

}

 

void flash_erase_page(uint32_t addr) {

  uint8_t msb = (addr & 0xFF0000) >> 16;

  uint8_t sreg;

 

  /* Disable interrupts */

  sreg = SREG;

  cli();

 

  SP_WaitForSPM();

 

  asm(

    "in    r19, %0                  \n\t"  /* Save RAMPZ */

    "out   %0,  %1                  \n\t"  /* Load RAMPZ with the MSB of the address */

    "movw  r24, %2                  \n\t"  /* Move low bytes for ZH:ZL to R25:R24 */

 

    "ldi   r20, %3                  \n\t"  /* Prepare the NVM command */

    "movw  ZL, r24                  \n\t"  /* Load Z with the pointer */

    "sts   %4, r20                  \n\t"  /* Load command into NVM Command register */

 

    "ldi   r18, %5                  \n\t"  /* Prepare Protect IO-register signature in R18. */

    "sts   %6, r18                  \n\t"  /* Enable Enable SPM operation */

    "spm                            \n\t"  /* Self Program */

 

    "clr   r1                       \n\t"  /* Clear R1 for GCC _zero_reg_ to function properly */

    "out   %0, r19                  \n\t"  /* Restore RAMPZ */

    :

    : "i" (ADDR_RAMPZ), "r" (msb), "r" (addr & 0xFFFF),

      "i" (NVM_CMD_ERASE_APP_PAGE_gc), "i" (ADDR_NVM_CMD),

      "i" (CCP_SPM_gc), "i" (ADDR_CCP)

  );

 

  SP_WaitForSPM();

 

  /* Re-enable interrupts */

  sei();

  SREG = sreg;

}

 

void flash_write_page(uint32_t addr, uint8_t *buf) {

  uint8_t msb = (addr & 0xFF0000) >> 16;

  uint8_t sreg;

 

  /* Disable interrupts */

  sreg = SREG;

  cli();

 

  SP_WaitForSPM();

 

  /* Load the flash buffer */

  asm(

    "in    r19, %0                  \n\t"  /* Save RAMPZ */

    "out   %0, r1                   \n\t"  /* Clear RAMPZ pointer */

    "clr   ZL                       \n\t"  /* Clear low byte of Z, to indicate start of page */

    "clr   ZH                       \n\t"  /* Clear high byte of Z, to indicate start of page */

 

    "out   %1, r1                   \n\t"  /* Clear RAMPX pointer */

    "movw  XL, %2                   \n\t"  /* Load X with data buffer address */

 

    "ldi   r20, %3                  \n\t"  /* Prepare NVM command code in R20. */

    "sts   %4, r20                  \n\t"  /* Load it into NVM command register */

 

    "ldi   r22, %5                  \n\t"  /* Load R22 with byte cont high if flash page is large. */

    "ldi   r21, %6                  \n\t"  /* Load R21 with byte count. */

 

    "SP_LoadFlashPage_1:            \n\t"

 

    "ld    r0, X+                   \n\t"  /* Load low byte from buffer into R0 */

    "ld    r1, X+                   \n\t"  /* Load high byte from buffer into R1 */

    "ldi   r18, %7                  \n\t"  /* Prepare Protect IO-register signature in R18. */

    "sts   %8, r18                  \n\t"  /* Enable Enable SPM operation */

    "spm                            \n\t"  /* Self Program */

    "adiw  ZL, 2                    \n\t"  /*  Move Z to next Flash word */

 

    "subi  r21, 1                   \n\t"  /* Decrement the word count */

    "sbci  r22, 0                   \n\t"

 

    "brne  SP_LoadFlashPage_1       \n\t"  /* Repeat until zero */

 

    "clr   r1                       \n\t"  /* Clear R1 for GCC _zero_reg_ to function properly */

    "out   %0, r19                  \n\t"  /* Restore RAMPZ */

    :

    : "i" (ADDR_RAMPZ),

      "i" (ADDR_RAMPX), "r" (buf),

      "i" (NVM_CMD_LOAD_FLASH_BUFFER_gc), "i" (ADDR_NVM_CMD),

      "i" ((APP_SECTION_PAGE_SIZE/2) >> 8), "i" ((APP_SECTION_PAGE_SIZE/2)&0xFF),

      "i" (CCP_SPM_gc), "i" (ADDR_CCP)

  );

 

  SP_WaitForSPM();

 

  /* Write the Flash Buffer to Flash Memory */

  asm(

    "in    r19, %0                  \n\t"  /* Save RAMPZ */

    "out   %0,  %1                  \n\t"  /* Load RAMPZ with the MSB of the address */

    "movw  r24, %2                  \n\t"  /* Move low bytes for ZH:ZL to R25:R24 */

 

    "ldi   r20, %3                  \n\t"  /* Prepare the NVM command */

    "movw  ZL, r24                  \n\t"  /* Load Z with the pointer */

    "sts   %4, r20                  \n\t"  /* Load command into NVM Command register */

 

    "ldi   r18, %5                  \n\t"  /* Prepare Protect IO-register signature in R18. */

    "sts   %6, r18                  \n\t"  /* Enable Enable SPM operation */

    "spm                            \n\t"  /* Self Program */

 

    "clr   r1                       \n\t"  /* Clear R1 for GCC _zero_reg_ to function properly */

    "out   %0, r19                  \n\t"  /* Restore RAMPZ */

    :

    : "i" (ADDR_RAMPZ), "r" (msb), "r" (addr & 0xFFFF),

      "i" (NVM_CMD_WRITE_APP_PAGE_gc), "i" (ADDR_NVM_CMD),

      "i" (CCP_SPM_gc), "i" (ADDR_CCP)

  );

 

  SP_WaitForSPM();

 

  /* Re-enable interrupts */

  sei();

  SREG = sreg;

}

 

void flash_read_page(uint32_t addr, uint8_t *buf) {

  /* Save RAMPZ, which is restored later */

  uint8_t msb = (addr & 0xFF0000) >> 16;

 

  /* Read a page of data into the buffer */

  asm(

    "in    r19, %0                  \n\t"  /* Save RAMPZ */

    "out   %0,  %1                  \n\t"  /* Load RAMPZ with the MSB of the address */

    "movw  ZL,  %2                  \n\t"  /* Load Z with Flash address. */

 

    "out   %3, r1                   \n\t"  /* Load RAMX with data pointer (zero register) */

    "movw  XL, %4                   \n\t"  /* Load X with data buffer address. */

 

    "ldi   r20, %5                  \n\t"  /* Prepare the NVM command */

    "sts   %6, r20                  \n\t"  /* Load command into NVM Command register */

 

    "ldi   r22, %7                  \n\t"  /* Load R22 with byte cont high if flash page is large. */

    "ldi   r21, %8                  \n\t"  /* Load R21 with byte count. */

 

    "SP_ReadFlashPage_1:            \n\t"

    "elpm  r24, Z+                  \n\t"  /* Load Flash bytes */

    "elpm  r25, Z+                  \n\t"

    "st    X+, r24                  \n\t"  /* Write Flash bytes */

    "st    X+, r25                  \n\t"

 

    "subi  r21, 1                   \n\t"  /* Decrement the word count */

    "sbci  r22, 0                   \n\t"

 

    "brne  SP_ReadFlashPage_1       \n\t"  /* Repeat until zero */

 

    "clr   r1                       \n\t"  /* Clear R1 for GCC _zero_reg_ to function properly */

    "out   %0, r19                  \n\t"  /* Restore RAMPZ */

    :

    : "i" (ADDR_RAMPZ), "r" (msb), "r" (addr & 0xFFFF),

      "i" (ADDR_RAMPX), "r" (buf),

      "i" (NVM_CMD_NO_OPERATION_gc), "i" (ADDR_NVM_CMD),

      "i" ((APP_SECTION_PAGE_SIZE/2) >> 8), "i" ((APP_SECTION_PAGE_SIZE/2)&0xFF)

  );

}