UltimaSerial Tech Blog: AVR32 Linker Script Example: To place a section of  codes into a certain physical memory location

Data logger


Windaq add-ons
Windaq Add-ons




Ultimaserial XChart




Ultimaserial Classroom

Sometimes, it is necessary to force a section of codes into a certain physical memory location. 

Assume your project is named as MyProject and you've got debug utilities imported so that you can use print_dbg_xxx

  1. From: C:\Program Files\Atmel\AVR Tools\AVR32 Toolchain\avr32\lib\ldscripts, locate the default linker script for the target MPU, for example, avr32elf_uc3a0512.x. There are several other linker scripts based on the configuration of your project, but we will use this one as our stepping stone

  2. Copy it to the root of MyProject

  3. Rename it to my_linker_script.lds

  4. Modify it. Note: keep in mind that UC3A0512 has 512K flash. If you are using a different MPU, you will need to adjust accordingly and the output will also be different 

    1. In MEMORY, change FLASH (rxai!w) : ORIGIN = 0x80002000, LENGTH = 512K to FLASH (rxai!w) : ORIGIN = 0x80002000, LENGTH = 512K-10K-8K

      1. All AVR32 project will be linked to 0x80002000 if no customized linker script presented!

      2. The default linker script has a minor mistake: it didn't take the size of the USB DFU bootloader into account when it specifies the length, but this will never cause a problem since you will need to create a huge project to consume all 512K flash before you will fall into this trap :)

    2. In MEMORY, add FLASH2 (rxai!w) : ORIGIN = 0x80002000+512K-10K-8K, LENGTH = 10K

    3. In SECTIONS, add .mytest_section : {*(mytest_section )} >FLASH2 AT>FLASH2

    4. Now the linker script should be like this:

      FLASH (rxai!w) : ORIGIN = 0x80002000, LENGTH = 512K-10K-8K
      FLASH2 (rxai!w) : ORIGIN = 0x80002000+512K-10K-8K, LENGTH = 10K

      CPUSRAM (wxa!ri) : ORIGIN = 0x00000000, LENGTH = 64K
      USERPAGE : ORIGIN = 0x80800000, LENGTH = 512
      FACTORYPAGE : ORIGIN = 0x80800200, LENGTH = 512

      /* Read-only sections, merged into text segment: */
      PROVIDE (__executable_start = 0x80000000); . = 0x80000000;
      .mytest_section : {*(.mytest_section )} >FLASH2 AT>FLASH2
      .interp : { *(.interp) } >FLASH AT>FLASH
      .reset : { *(.reset) } >FLASH AT>FLASH

    5. Save it

    6. Side note: If you  ever wonder what makes the Atmel's USB DFU links to 0x80000000 instead of 0x80002000, follow these steps to take a look at the linker script of USB Bootloader example provided by AVR32 Studio.

      1. Make sure you have AVR32 Studio 2.1 or higher. Earlier AVR32 Studio doesn't provide the example.

      2. Follow AVR32Studio -> File -> New -> other -> AVR32 Example Project -> UC3A0512 -> EVK1100->SERVICE->USB DFU ISP example. 

      3. Open the src folder and you will find the linker script, link_at32uc3a-isp.lds

        1. You may notice the linker script is in a different location as recommended in Atmel's application note. Don't worry, it is taken care in the directive for the linker. Follow USB DFU ISP example->Properties->Tool Settings->AVR32/GNU C Linker->Miscellaneous, you will see the directive of -T../src/link_at32uc3a-isp.lds

      4. You will see its MEMORY is different from the one above

        FLASH (rxai!w) : ORIGIN = 0x80000000, LENGTH = 0x00080000
        INTRAM (wxa!ri) : ORIGIN = 0x00000004, LENGTH = 0x0000FFFC
        USERPAGE : ORIGIN = 0x80800000, LENGTH = 0x00000200

      5. Please read http://atmel.com/dyn/resources/prod_documents/doc32131.pdf for more detail regarding this AVR32 USB DFU ISP example

  5. Follow MyProject->Properties->Tool Settings->AVR32/GNU C Linker->Miscellaneous. and add "-T../my_linker_script.lds" to Other options (-Xlinker [option])

  6. Save it

  7. In MyProject->src->main.c

    1. Before the body of main(), add int test_call (void) __attribute__((__section__(".mytest_section "))); 

    2. Before the body of main(), add int test_call (void) {;}. If you'd like you can add some meaningful codes to test it.

    3. Inside the body of main (), after the initialization codes, add print_dbg("\ntest_call locates at: "); print_dbg_hex((long)test_call);  The purpose of this to print out the address of test_call.

    4. Right after the above lines, add print_dbg("\nmain locates at: "); print_dbg_hex((long)main); The purpose of this to print out the address of main.

    5. Save it

  8. Build the project and run it

  9. If you run HyperTerminal and connects PC's com port to the com port of your EVK1100, you will see the output "test_call locates at 0x8007D800" then something like "main locates at 0x80020578"

    1. Keep in mind that UC3A0512 has 512K flash. If you are using a different MPU, you will need to adjust the linker script accordingly and the output will also be different.

    2. Do not assume main, which is the entry point of the whole project, will be allocated to the very first address of the code segment, 0x80002000! since FreeRTOS and other initialization must take place before main() is invoked.

  10. Simple math shows: 0x80002000+512K-10K-8K=0x80002000+0x80000-0x2800-0x2000=0x8007D800, so test_call () is allocated at the beginning of the last 10K of internal FLASH, away from the rest of codes.

    1. Note: If test_call needs to access some FLASH-based constants, watch out, it may not be in this last 10K of the internal FLASH. 

    2. If you wish to learn more about linker scripts, visit http://www.math.utah.edu/docs/info/ld_toc.html#SEC4 and http://www.davehylands.com/avi/linker_scripts.htm 

  11. Want to verify it in machine codes? Read on...

  12. We use this command line to convert the .elf file to .hex for easy reading, avr32-objcopy -O ihex MyProject.elf MyProject.hex

    1. If you are more familiar with .bin file format, use command line avr32-objcopy -O binary MyProject.elf MyProject.bin instead and skip the discussion below.

  13. Use Wiki as your reference to study Hex file format

  14. Open MyProject.hex with any text editor, the first line reads :0200000480007A.

  15. With the help from Wiki, we know that 04 is the Extended linear Address Record, and its address field is 8000, thus the address in next line should be added with 0x8000000 to form a 32-bit address.

  16. The second line something like :10200000E08F100000000000000000000000000051

  17. Combining the extended linear address record of 0x80000000, we know the actual codes starts at 0x80002000

    1. Now you see the very first line bypasses the regular USB_DFU section

  18. If you use ":02" as the keyword to search the .hex file, you will find :02000004800179

  19. This is where the codes has to move to a consecutive memory location beyond 0x8000FFFF, starting at 0x80010000

  20. You will even find :02000004800278, when the codes have to cross 0x80020000

  21. Somewhere down the .hex file, you will find a line like this, :02000004800773

  22. Aha, this is another Extended linear Address Record (04), indicating the address in next line should add 0x8007000 to form a 32-bit address

  23. The very next line is something like this :10D80000EBCD40801A97203D1898EF68FFF83198CB

  24. Simple math shows: 0xD800+0x80070000=0x8007D800 

    1. If you keep track of the addresses, you will notice there is a gap between 0x8007D800 and the last used address.

  25. We caught the entry point of our test_call!

  26. BTW, take a look at the line before the end of the file, :040000058000200057, it specifies the start address of 0x80002000



Last update: 02/24/12