SD bootloader for Atmel AT32UC3 devices
Jordi Bartolomé 3-8-2013

1-Introduction
2-Environment and tools
3-Steps
   3.1-Modify the USB Host Mass Storage Bootlader source
   3.2-Modify the main application
   3.3-Create the firmware upgrade file including the application
   3.4-Programming sequence

4-Download


1-Introduction

In this note I describe the steps I followed to develop the SD bootloader I use on my AVR32 AT32UC3A0256 board. It is entirely based on the USB MSC bootloader described in Atmel's Application Note 32758 "AVR32 UC3 USB Host Mass Storage Bootloader" ( doc7818.pdf and avr32758.zip ). This Atmel's application note includes the source code, scripts, explanations, and other resources necessary for building and using the USB MSC bootloader.

The SD bootloader is a tiny application placed at the beginning of the flash memory address space, used to burn new firmware on the microcontroller without the need of any special programmer. It is always executed when the system boots, and it checks if a special flag is present in the user flash. If not, it moves the program counter to the application start address to execute it. If it is present, it reads the .uc3 firmware upgrade file from the SD card and burns it into flash memory replacing the old application ( without overwriting or erasing the SD bootloader ! ). Once new firmware is burnt, bootloader clears that special flag and resets the system. When the system reboots, as the special flag has been cleared, the bootloader will executing the new application. Usually this special flag is set by the application each time the firmware needs to be upgraded ( for example when the user selects a "upgrade firmware" option ).

I developed the SD bootloader for a custom board, which has some differences with the EVK1100 ( Atmel's Application Note 32758 is based on EVK1100 board ). Most important differences are:
- Debug USART is AVR32_USART0 instead of AVR32_USART1 .
- The SD uses chip select AVR32_SPI1_NPCS_0_0_PIN instead of AVR32_SPI1_NPCS_1_0_PIN .
- Has no USB port.
- Includes a compatible HD44780 LCD to show messages to the user during the firmware upgrade.
- Does not use Atmel's DFU (Device Firmware Upgrade bootloader ) . So the SD bootloader programming is directly done with the JTAG ICE mk II, or ATMELs ICE hardware programmer. The source code of the SD bootlader in a board that uses Atmel's DFU should be identical to it, with the only difference of the memory addresses where the SD bootloader is placed ( that implies modifying the DFU to jump to the SD bootloader once it finishes ). The custom board has the SD Bootloader and then the application, while a board with DFU would have the DFU, then the SD Bootloader and then the application.


The user interface of the device. The SD bootloader is enabled when the user presses a button
 
2-Environment and tools

I started developing the SD bootloader on AVR Studio 32 on Linux Ubuntu , but after experiencing some problems with my JTAG ICE mk II programmer I decided to move to Atmel Studio 6 ( V.2674 ) with AVR32/GNU C Compiler on Windows 7 Home Premium which has an IDE based on Microsoft Visual Studio which is perfect and very comfortable to use.

At the end of this note there is a download section with the source code of the SD bootloader and with other files which may be useful to understand how it works.

 
3-Steps

The SD bootlader development can be divided in these steps:
- Modify the USB Host Mass Storage Bootlader source, included in avr32758.zip. Modifications consist on reading the uc3 upgrade file from the SD card instead from the USB Host Mass Storage unit.
- Modify the main application to move it to an upper flash address to make space for the SD bootloader at the beginning of the flash memory. The source code should be also modified to allow it setting the flags nedded to start the SD bootloader once the system is reset.
- Create the firmware upgrade uc3 with the application.
- Program the application and the SD bootloader into microcontroller's flash memory.

 

3.1-Modify the USB Host Mass Storage Bootlader source

The source code of the USB Host Mass Storage Bootloader application is available on the evk1100-start-msc-bl-example folder of avr32758.zip file provided by Atmel . Some changes must be done to it to make it take the upgrade file from the SD card instead of the USB. First of all I configured the ports of some of the peripherals which are different on my board. I started setting the right SPI I/O pins on "\at32uc3A0512-ms-isp\src\BOARDS\EVK1100\evk1100.h" :

           ...
    // custom board SD SPI mapping ( only NPCS PIN is different )
    #define SD_MMC_CARD_DETECT_PIN      AVR32_PIN_PA02
    #define SD_MMC_WRITE_PROTECT_PIN    AVR32_PIN_PA07

    #define SD_MMC_SPI                  (&AVR32_SPI1)
    #define SD_MMC_SPI_NPCS             0 
    #define SD_MMC_SPI_SCK_PIN          AVR32_SPI1_SCK_0_0_PIN
    #define SD_MMC_SPI_SCK_FUNCTION     AVR32_SPI1_SCK_0_0_FUNCTION
    #define SD_MMC_SPI_MISO_PIN         AVR32_SPI1_MISO_0_0_PIN
    #define SD_MMC_SPI_MISO_FUNCTION    AVR32_SPI1_MISO_0_0_FUNCTION
    #define SD_MMC_SPI_MOSI_PIN         AVR32_SPI1_MOSI_0_0_PIN
    #define SD_MMC_SPI_MOSI_FUNCTION    AVR32_SPI1_MOSI_0_0_FUNCTION
    #define SD_MMC_SPI_NPCS_PIN         AVR32_SPI1_NPCS_0_0_PIN 
    #define SD_MMC_SPI_NPCS_FUNCTION    AVR32_SPI1_NPCS_0_0_FUNCTION 
           ...

I created a defines.h file where I placed some global constants and also the defines with the USART ports used to send traces:

           ...
    // USART connected to the MAX 232
    #define serial_232_PORT_USART              (&AVR32_USART0)
    #define serial_232_PORT_USART_RX_PIN       AVR32_USART0_RXD_0_0_PIN
    #define serial_232_PORT_USART_RX_FUNCTION  AVR32_USART0_RXD_0_0_FUNCTION
    #define serial_232_PORT_USART_TX_PIN       AVR32_USART0_TXD_0_0_PIN
    #define serial_232_PORT_USART_TX_FUNCTION  AVR32_USART0_TXD_0_0_FUNCTION
    #define serial_232_PORT_USART_IRQ          AVR32_USART0_IRQ
    #define serial_232_PORT_USART_BAUDRATE     57600 
           ...

As my AVR32 AT32UC3A0256 board has no USB, I removed all the USB related code, and I commented some lines on the "\at32uc3A0512-ms isp\src\SERVICES\USB\CLASS\MASS_STORAGE\EXAMPLES\ISP\isp.c" ( the main bootloader file ) :

 
    // #include "conf_usb.h"
    // #include "usb_task.h"
    // #if USB_HOST_FEATURE == ENABLED
    // #include "host_mass_storage_task.h"
    // #endif

    ...

    //#if USB_HOST_FEATURE == ENABLED
    static crc32_table_t crc32_table;
    //#endif

    ...
    // usb_task_init();
    // usb_task(); 
    // #if USB_HOST_FEATURE == ENABLED
    // host_mass_storage_task(); // USB Host Mass Storage Task is not used
    isp_task();  // Don't comment that line, it is the main programming task! 
    // #endif
 

I also added the SPI, USART and GPIO driver folders to the project files structure :

 

      "\at32uc3A0512-ms-isp\src\DRIVERS\SPI"
      "\at32uc3A0512-ms-isp\src\DRIVERS\USART"
      "\at32uc3A0512-ms-isp\src\DRIVERS\GPIO"
      "\at32uc3A0512-ms-isp\src\COMPONENTS\MEMORY\SD_MMC"

 

In order these new components and drivers get compiled and linked each time the application is built , I had to add some entries in the Project Properties Window : "Project > at32uc3A0512-ms-isp Properties" "AVR32/GNU C Compiler > Directories" :

 

      "../src/DRIVERS/USART"
      "../src/DRIVERS/GPIO"
      "../src/DRIVERS/SPI"
      "../src/COMPONENTS/MEMORY/SD_MMC"

 

Also added some files in the main application folder on "\at32uc3A0512-ms-isp\src\ SERVICES\ USB\ CLASS\ MASS_STORAGE\ EXAMPLES\ISP\ INTC" with the driver of the hd44780 LCD and the defines of some GPIO ports :

 

      defines.h
      gpio_ports.h
      lcd_hd44780.c
      lcd_hd44780.h

 

Despite it is not necessary these directories can be deleted and removed from the Properties Window ( I removed them and the bootloader is still working ) :

 

../src/SERVICES/USB/CLASS/MASS_STORAGE/HOST_MEM ../src/SERVICES/USB/CLASS/MASS_STORAGE/SCSI_DECODER

 

I also added some includes and peripheral initialization routines in the main application file "\at32uc3A0512-ms-isp\src\SERVICES\USB\CLASS\MASS_STORAGE\EXAMPLES\ISP\isp.c" to allow the access to the SD, LCD and USART from the application:

 
    // extra includes
    #include "gpio.h"
    #include "usart.h"
    #include "spi.h"
    #include "sd_mmc_spi.h"
    #include "lcd_hd44780.h"

    ...

    // Synth Core  23-11-2011  Tolaemon
    // Function: void FILE_SYS_SD_MMC_resources_init()
    // Function which prepares the SPI to be used with
    // the MMC_SD card
    // Receives:
    // Returns:
    // Note:
    //     Call before starting the OS scheduler
    void FILE_SYS_SD_MMC_resources_init(void){

        // GPIO map for SPI.
        static const gpio_map_t SD_SPI_GPIO_MAP =
        {
            { SD_MMC_SPI_SCK_PIN, SD_MMC_SPI_SCK_FUNCTION },  // SPI Clock.
            { SD_MMC_SPI_MISO_PIN, SD_MMC_SPI_MISO_FUNCTION },// MISO.
            { SD_MMC_SPI_MOSI_PIN, SD_MMC_SPI_MOSI_FUNCTION}, // MOSI.
            { SD_MMC_SPI_NPCS_PIN, SD_MMC_SPI_NPCS_FUNCTION } // Chip Select NPCS.
        };

        // Options for SPI.
        spi_options_t spiOptions =
        {
            .reg          = SD_MMC_SPI_NPCS,
            .baudrate     = 1200000,//SD_MMC_SPI_MASTER_SPEED,
            .bits         = 8,
            .spck_delay   = 0,
            .trans_delay  = 0,
            .stay_act     = 1,
            .spi_mode     = 0,
            .modfdis      = 1
        };

        // assign I/Os to SPI.
        gpio_enable_module(SD_SPI_GPIO_MAP,
        sizeof(SD_SPI_GPIO_MAP) / sizeof(SD_SPI_GPIO_MAP[0]));

        // initialize as master.
        spi_initMaster(SD_MMC_SPI, &spiOptions);

        // set selection mode: variable_ps, pcs_decode, delay.
        spi_selectionMode(SD_MMC_SPI, 0, 0, 0);

        // enable SPI.º1
        spi_enable(SD_MMC_SPI);

        sd_mmc_spi_init(spiOptions,FOSC0);

    }//SD_MMC_resources_init

    ...

    int main(void)
    {
        
        sys_clk_gen_start();
        crc32_init(&crc32_table);
        // usb_task_init();

        // start the crystal oscillator 0 and switch the main clock to it.
        // pm_switch_to_osc0(&AVR32_PM, FOSC0, OSC0_STARTUP);

        gpio_local_init();

        // initialize use GIPIO pins Enable the local bus interface
        // for GPIO. gpio_local_init( ) must be called first!!
        gpio_local_enable_pin_output_driver(SCOREB_BUS_IO_01);
        gpio_local_enable_pin_output_driver(SCOREB_BUS_IO_02);
        ...
        gpio_local_enable_pin_output_driver(SCOREB_BUS_IO_34);
        gpio_local_enable_pin_output_driver(SCOREB_BUS_IO_35);

      
        rs232_init();

        FILE_SYS_SD_MMC_resources_init();

        LCD_HD44780_init();
        LCD_HD44780_clear();
        LCD_HD44780_write("Fwre Update",1);


         while (TRUE)
        {
        // usb_task();
    // #if USB_HOST_FEATURE == ENABLED
        // host_mass_storage_task();
        isp_task();
    // #endif
        }
    }
 

Also modified the ISP main task to show some traces and messages through the serial port and LCD. This part of the code is the main state machine which implements the In System Flash Programming procedure :

 
    static void isp_task(void)
    {
      #define PROGRAM_START_PAGE  (PROGRAM_START_OFFSET / AVR32_FLASHC_PAGE_SIZE)
      #define HEADER_SIZE         (sizeof(ID) + sizeof(UUID) + sizeof(crc32))

      volatile avr32_usart_t  *usart;

      static const U8 ID[] = ISP_FW_UPGRADE_FILE_ID;
      static const U8 UUID[] = ISP_FW_UPGRADE_FILE_UUID;

      static enum
      {
        ISP_STATE_ERROR               = -1,
        ISP_STATE_IDLE                =  0,
        ISP_STATE_MOUNT               =  1,
        ISP_STATE_CHECK               =  2,
        ISP_STATE_CRC32               =  3,
        ISP_STATE_ERASE_AND_VERIFY    =  4,
        ISP_STATE_PROGRAM_AND_VERIFY  =  5
      } isp_state = ISP_STATE_IDLE;

      static U32 crc32;

    #if __GNUC__ && __AVR32__
      __attribute__((__aligned__(4)))
    #elif __ICCAVR32__
      #pragma data_alignment = 4
    #endif
      U8 buffer[AVR32_FLASHC_PAGE_SIZE];
      volatile U64 *flash_ptr;
      S32 i;

        usart = serial_232_PORT_USART;

      /* not using USB
      if (!Is_host_ms_configured())
      {
        isp_state = ISP_STATE_IDLE;
        return;
      } */
      
     switch (isp_state)
     {
      case ISP_STATE_ERROR:
        break;

      case ISP_STATE_IDLE:
        LCD_HD44780_write("MOUNT",2);
        usart_write_line(usart, "ISP_STATE_MOUNT\r\n");
        isp_state = ISP_STATE_MOUNT;
        break;

      case ISP_STATE_MOUNT:
        nav_reset();
        if (!nav_drive_set(0) ||
        !nav_partition_mount())
        {
          LCD_HD44780_write("ERR: MOUNT 1",2);
          usart_write_line(usart, "ERR: ISP_STATE_MOUNT 1\r\n");
          isp_state = ISP_STATE_ERROR;
          break;
        }
        LCD_HD44780_write("CHECK",2);
        usart_write_line(usart, "ISP_STATE_CHECK\r\n");
        isp_state = ISP_STATE_CHECK;
        break;

      case ISP_STATE_CHECK:
        if (!nav_filelist_single_enable(FS_FILE) ||
        !nav_filelist_set(0, FS_FIND_NEXT) ||
        !nav_file_name(ISP_FW_UPGRADE_FILENAME, 0, FS_NAME_CHECK, TRUE))
        {
          LCD_HD44780_write("ERR: CHECK 1",2);
          usart_write_line(usart, "ERROR: ISP_STATE_CHECK 1\r\n");
          isp_state = ISP_STATE_ERROR;
          break;
        }
          
        i = nav_file_lgt() - HEADER_SIZE;
        if (i < 0 ||
        i > flashc_get_flash_size() - PROGRAM_START_OFFSET ||
        !file_open(FOPEN_MODE_R) ||
        file_read_buf(buffer, sizeof(ID)) != sizeof(ID) ||
        memcmp(buffer, ID, sizeof(ID)) ||
        file_read_buf(buffer, sizeof(UUID)) != sizeof(UUID) ||
        memcmp(buffer, UUID, sizeof(UUID)) ||
        file_read_buf((U8 *)&crc32, sizeof(crc32)) != sizeof(crc32))
        {
          LCD_HD44780_write("ERR: CHECK 2",2);
          usart_write_line(usart, "ERROR: ISP_STATE_CHECK 2\r\n");
          isp_state = ISP_STATE_ERROR;
          break;
        }

        crc32_reset();
        LCD_HD44780_write("CRC32",2);
        usart_write_line(usart, "ISP_STATE_CRC32\r\n");
        isp_state = ISP_STATE_CRC32;
        break;

      case ISP_STATE_CRC32:

         switch (file_eof())
         {
            case 0x00:
              if (!(i = file_read_buf(buffer, sizeof(buffer))))
              {
                LCD_HD44780_write("ERR: CRC32 1",2);
                usart_write_line(usart, "ERROR: ISP_STATE_CRC32 1\r\n");
                isp_state = ISP_STATE_ERROR;
                break;
              }
              crc32_iterate_buffer(buffer, i);
              break;
            case 0x01:
              if (crc32 != crc32_get_value())
              {
                LCD_HD44780_write("ERR: CRC32 2",2);
                usart_write_line(usart, "ERROR: ISP_STATE_CRC32 2\r\n");
                isp_state = ISP_STATE_ERROR;
                break;
              }
              LCD_HD44780_write("ERASE_AND_VERIFY",2);
              usart_write_line(usart, "ISP_STATE_ERASE_AND_VERIFY\r\n");
              isp_state = ISP_STATE_ERASE_AND_VERIFY;
              break;
            case 0xFF:
              LCD_HD44780_write("ERR: CRC32 3",2);
              usart_write_line(usart, "ERROR: ISP_STATE_CRC32 3\r\n");
              isp_state = ISP_STATE_ERROR;
              break;
         }// switch (file_eof())
         break;

      case ISP_STATE_ERASE_AND_VERIFY:

        for (i = flashc_get_page_region(PROGRAM_START_PAGE); i < AVR32_FLASHC_REGIONS; i++)
        {
          flashc_lock_region(i, FALSE);
          if (flashc_is_region_locked(i))
          {
            LCD_HD44780_write("ERR: E_VERIFY 1",2);
            usart_write_line(usart, "ERROR: ISP_STATE_ERASE_AND_VERIFY 1\r\n");
            isp_state = ISP_STATE_ERROR;
            return;
          }
        }

        for (i = flashc_get_page_count() - 1; i >= PROGRAM_START_PAGE; i--)
        {
          if (!flashc_erase_page(i, TRUE))
          {
            LCD_HD44780_write("ERR: E_VERIFY 2",2);
            usart_write_line(usart, "ERROR: ISP_STATE_ERASE_AND_VERIFY 2\r\n");
            isp_state = ISP_STATE_ERROR;
            return;
          }
        }

        if (!file_seek(HEADER_SIZE, FS_SEEK_SET))
        {
          LCD_HD44780_write("ERR: E_VERIFY 3",2);
          usart_write_line(usart, "ERROR: ISP_STATE_ERASE_AND_VERIFY 3\r\n");
          isp_state = ISP_STATE_ERROR;
          break;
        }
        isp_state = ISP_STATE_PROGRAM_AND_VERIFY;
        break;

      case ISP_STATE_PROGRAM_AND_VERIFY:

         switch (file_eof())
         {
           case 0x00:
              if (!(i = file_getpos()))
              {
                LCD_HD44780_write("ERR: P_VERIFY 1",2);
                usart_write_line(usart, "ERROR: ISP_STATE_PROGRAM_AND_VERIFY 1\r\n");
                isp_state = ISP_STATE_ERROR;
                break;
              }
              flash_ptr = (U64 *)(PROGRAM_START_ADDRESS + i - HEADER_SIZE);
              if (!(i = file_read_buf(buffer, AVR32_FLASHC_PAGE_SIZE)))
              {
                LCD_HD44780_write("ERR: P_VERIFY 2",2);
                usart_write_line(usart, "ERROR: ISP_STATE_PROGRAM_AND_VERIFY 2\r\n");
                isp_state = ISP_STATE_ERROR;
                break;
              }
              memset(&buffer[i], 0xFF, AVR32_FLASHC_PAGE_SIZE - i);
              flashc_clear_page_buffer();
              for (i = 0; i < AVR32_FLASHC_PAGE_SIZE / sizeof(*flash_ptr); i++)
                      flash_ptr[i] = ((U64 *)buffer)[i];
              flashc_write_page(-1);
              if (memcmp((void *)flash_ptr, buffer, AVR32_FLASHC_PAGE_SIZE))
              {
                LCD_HD44780_write("ERR: P_VERIFY 3",2);
                usart_write_line(usart, "ERROR: ISP_STATE_PROGRAM_AND_VERIFY 3\r\n");
                isp_state = ISP_STATE_ERROR;
                break;
              }
              break;
              
           case 0x01:
              file_close();                              
              // Clear the address AVR32_FLASHC_USER_PAGE_ADDRESS+ISP_FORCE_OFFSET 
              // at ( 0x808001F8 ) where the key that activates de ISP programming
              // is stored
              flash_ptr = (U64 *)AVR32_FLASHC_USER_PAGE;
              memcpy(buffer, (void *)flash_ptr, AVR32_FLASHC_PAGE_SIZE);
              *(U32 *)&buffer[ISP_FORCE_OFFSET] = 0xFFFFFFFF;
              flashc_clear_page_buffer();
              for (i = 0; i < AVR32_FLASHC_PAGE_SIZE / sizeof(*flash_ptr); i++)
                      *flash_ptr++ = ((U64 *)buffer)[i];
              flashc_erase_user_page(FALSE);
              flashc_write_user_page();
                  
              // enables the watchdog to reset de system
              Disable_global_interrupt();
              AVR32_WDT.ctrl = AVR32_WDT_CTRL_EN_MASK |
                       (10 << AVR32_WDT_CTRL_PSEL_OFFSET) |
                       (AVR32_WDT_KEY_VALUE << AVR32_WDT_CTRL_KEY_OFFSET);
              asm ("");
              AVR32_WDT.ctrl = AVR32_WDT_CTRL_EN_MASK |
                       (10 << AVR32_WDT_CTRL_PSEL_OFFSET) |
                       ((~AVR32_WDT_KEY_VALUE << AVR32_WDT_CTRL_KEY_OFFSET) \ 
                                                       & AVR32_WDT_CTRL_KEY_MASK);
              
              // wait until the system resets                   
              LCD_HD44780_write("READY RESET!",2);
              usart_write_line(usart, "READY RESET!\r\n");
              while (1){
                   usart_write_line(usart, ".");  
              };
           case 0xFF:
              isp_state = ISP_STATE_ERROR;
              break;
         }//switch (file_eof())
         break;

       }//swtich (isp_state)

    }//static void isp_task(void)
 

To enable de SD card and disable the USB Mass Storage, the LUN list with the storage devices in "..\at32uc3A0512-ms-isp\src\SERVICES\USB\CLASS\MASS_STORAGE\EXAMPLES\ISP\CONF\conf_access.h" has to be modified as shown:

           ...
    #define LUN_0                DISABLE //!< On-Chip Virtual Memory.
    #define LUN_1                DISABLE //!< AT45DBX Data Flash.
    #define LUN_2                ENABLE  //!< SD/MMC Card over SPI.
    #define LUN_3                DISABLE
    #define LUN_4                DISABLE
    #define LUN_5                DISABLE
    #define LUN_6                DISABLE
    #define LUN_7                DISABLE
    #define LUN_USB              DISABLE   //!< Host Mass-Storage Memory.
           ...

When DFU is used, the DFU binary must be placed at the flash address 0x80000000 in order it is executed first, each time the system boots. As it nearly takes 0x2000 bytes the SD bootloader should be placed at address 0x80002000 where the DFU should jump after its execution. But as my board does not use DFU, the SD bootloader start address can be set to 0x80000000, what will cause that the SD bootlader is executed each time the system resets. To place the SD bootloader at the 0x80000000 address, I had to modify the boot.x file of the bootloader project, present at "\at32uc3A0512-ms-isp\ src\ SERVICES\ USB\ CLASS\ MASS_STORAGE\ EXAMPLES\ ISP\ BOOT\boot.x" . This file contains the assembler bootloader start code, that checks if the special user flash flag is present, and if it has to burn the new firmware or jump to the application start address. To place the bootlader at the address 0x80000000 the following line in boot.x must be commented:

 
      //.org 0x00002000 // line commented to place the SD bootloader start address at 0x80000000, keep it in 0x80002000 if DFU bootloader used

Image taken from Atmel's 32758 Application Note showing the memory organization.
 

As seen in the picture there are 32Kbs for the SD bootloader, and probably this is too much, so the application start address could be moved to a lower address, because the SD booloader takes less than 32KB and this would give more free space for the application. Modifying the PROGRAM_START_OFFSET in the application and the bootloader would be enough to do this.

I got a "multiple definition of `_start'" linker error when building the bootloader, and it was because the compiler found that the _start entry was defined in another part of the project ( this label is present in both boot.x file and crt0 ) . To avoid that problem I had to check the option "Do not use Standard start files -nostartfiles" in "Project>Properties" , "AVR32/GNU Linker>General" tab .

The SD bootloader must also know where to jump to execute the application, so the PROGRAM_START_OFFSET address in "\at32uc3A0512-ms-isp\ src\ SERVICES\ USB\ CLASS\ MASS_STORAGE\ EXAMPLES\ISP\ CONF\ conf_isp.h" file must be modified to set the application start address to 0x80008000

           ...
    #define PROGRAM_START_ADDRESS         (AVR32_FLASH_ADDRESS + PROGRAM_START_OFFSET)
    #define PROGRAM_START_OFFSET          0x00008000
           ...
 
 
3.2-Modfify the main application

I also had to do some changes in the main application to make it work with the bootloader. First, it had to place it at the right application start address, to make space for the SD bootloader. The bootloader will jump to that address when finishes its execution. I edited the file "\MyApplication\ src\ SOFTWARE_FRAMEWORK\ UTILS\ conf_isp.h" in the same way I did in the SD bootloader :

           ...
    #define PROGRAM_START_ADDRESS         (AVR32_FLASH_ADDRESS + PROGRAM_START_OFFSET)
    #define PROGRAM_START_OFFSET          0x00008000
           ...

I also added the file trampoline.x to the project. As said in the application note ( Ap Note AVR32758 Section 6.1.1 Taking care of the bootloader overlay Page 5 ) the function of the trampoline.x is " to avoid that application code is placed inside the bootloaders area" for that reason "a trampoline code must be placed at the reset vector (80000000h) that simply jumps to the beginning of the user application (80008000h). This trampoline code ensures that the application code starts at 80008000h and is located above this address." To execute trampoline.x when the system boots the program entry has to point to _trampoline . To do that use the option "Project>Properties" and in the tab "AVR32/GNU Linker>" set "Linker Flags:-Wl,-e,_trampoline".

As explained at te beginning of this note, the SD bootloader checks if a special flag is present in the user flash to know if has to burn a new firmware from the SD or not. If the special flag is present it takes the .uc3 file from the SD and programs it into flash ( it only burns the main application ). If it is not present it jumps to the application start address. So to make the SD bootloader to program a new firmware on the device, the main application must do two things: first, write the special firmware update flag in the user flash, second, reset the system. I added this code in the main application to do that, and It is called each time the firmware must be udpdated :

 
    // Function: void prepare_for_firmware_upgrade()
    // Forces the necessary values in Flash in order the bootloader detects 
    // that must perform a new firmware upgrade. Once these flags are set 
    // to execute the bootloader the system is reset with the watchdog in order
    // Receives:
    // Returns
    // By value:
    void prepare_for_firmware_upgrade(){
    
        // write at destination (AVR32_FLASHC_USER_PAGE + ISP_FORCE_OFFSET) the value
        // ISP_FORCE_VALUE. Size of ISP_FORCE_VALUE is 4 bytes.
        flashc_memset32(AVR32_FLASHC_USER_PAGE+ISP_FORCE_OFFSET, ISP_FORCE_VALUE, 4, TRUE);
    
        // enables the watchdog to reset de system
        Disable_global_interrupt();
        AVR32_WDT.ctrl = AVR32_WDT_CTRL_EN_MASK |
        (10 << AVR32_WDT_CTRL_PSEL_OFFSET) |
        (AVR32_WDT_KEY_VALUE << AVR32_WDT_CTRL_KEY_OFFSET);
        asm ("");
        AVR32_WDT.ctrl = AVR32_WDT_CTRL_EN_MASK |
        (10 << AVR32_WDT_CTRL_PSEL_OFFSET) |
        ((~AVR32_WDT_KEY_VALUE << AVR32_WDT_CTRL_KEY_OFFSET) & AVR32_WDT_CTRL_KEY_MASK);
    
        // wait untill watchdog resets the system
        while (1);

    }//prepare_for_firmware_upgrade
 

Also added the defines with the value of the update flag and the name of the firmware update file in "\MyApplication\ src\ SOFTWARE_FRAMEWORK\ UTILS\ conf_isp.h" To use another update file name simply change the value of ISP_FW_UPGRADE_FILENAME constant and the gen_uc3 script output file name :

           ...
    // added to support the bootloader 24-07-2013
    #define ISP_FW_UPGRADE_FILENAME       "avr32fwupgrade.uc3"

    #define ISP_FW_UPGRADE_FILE_ID        {'A', 'V', 'R', '3', '2'}
    #define ISP_FW_UPGRADE_FILE_UUID      {0xA3, 0x21, 0xB4, 0x20,                 \
        0x3E, 0xE9,                             \
        0x11, 0xDD,                             \
        0xAE, 0x16,                             \
    0x08, 0x00, 0x20, 0x0C, 0x9A, 0x66}
           ...

3.3-Create the firmware upgrade file including the application

Next step wast to create the .uc3 upgrade file with the main application. Atmel provides the gen_uc3.cmd command line script ( .sh for linux and .cmd for Windows ) to generate that .uc3 file from the .hex file ( the .hex file is generated after the build process and can be found on "\MyApplication\Release" or "\MyApplication\Debug" folder ). To use it, simply type the name of the script followed by the name of the .hex, "gen_uc3 MyApplication.hex" and it will automaticaly generate the .uc3 update file. This script needs the srecord (srec_cat) utility to process the hex file. I didn't have it, so I had to download it from SourceForge and create an enviroment path variable pointing to it (another option could be saving it in the windows folder ) to make it visible to the script. If you get a "srec_cat: warning: option "-MAximum" is deprecated, use "-MAximum-Address" instead" don't worry, it is only a warning.

The SD card must be formated on FAT12, FAT16 or FAT32 file system and must only contain the upgrade .uc3 file, so don't place any other file or folder in the SD card ( check that there are no other system hidden folders or files on it ).

Once the avr32fwupgrade.uc3 file is created it has to be saved on the SD card. Then insert the SD card on the board and call the prepare_for_firmware_upgrade() function from the application. Doing that sets the special flags and resets the system. After the reset, the SD bootloader detects that the flags has been modified and then reads the .uc3 file and writes it to flash.

Despite it is the same than the one included on Atmel's avr32758.zip, here you can download the script I use to generate the .uc3 files.

 
3.4-Programming sequence

These are the steps I follow to program the bootloader and the application on my board for first time. I use my JTAG ICE mkII or Atmel ICE programmer with Atmel Studio, but this is not the only way, there are other possible sequences, for example if you are using the DFU bootlader ( check Atmel's docs for more information ) :
  - Erase chip ( on "Tools > Device programming > Memories > Erase chip " ) .
  - Program the application with mkII or ICE programmer ( on "Tools > Device programming > Memories > Flash program" ).
  - Program the SD Bootlader with mkII or ICE programmer ( on "Tools > Device programming > Memories > Flash program" ).
  - Once the application is ready,we will be able to enable the SD bootlader ( setting up the proper FLASH FLAGS ) to program .uc3 files with the new firmware.

As the application is placed above the bootloader, it is programed first to avoid erasing the SD bootloader.

 
4-Download

Here you can download the SD Bootloader source code. As you will notice, it is the same at32uc3A0512-ms-isp project provided by Atmel but with the modifications explained in this page to make it work as an SD bootloader. Despite it's name, the project settings are configured to work on a AT32uc3A0256, so you'll also have to change this to make it work on your board.

 
It is recomended to get the last version of the documentation and resources provided by Atmel, but In case you can't find it or have problems on getting it you can download a copy of the version of the doc7818-MSC.pdf and avr32758.zip files i used in the project.
 

Go back to the documents section