cassemblylinkeravr32avr32-gcc

How to access global C variables from within standalone assembler modules with the GCC toolchain for Atmel AVR32?


I am currently developing software for the Atmel AVR32 AT32UC3C0512C. The software consists of C modules (.c files) as well as standalone assembler modules (.S files). My IDE is Microchip Studio 7.0.2594 (formerly known as Atmel Studio). The toolchain incorporates avr32-gcc 4.4.7.

In the assembler modules, I actually can access global variables that I have defined in the C modules, but the method I am using since many years seems quite dumb to me:

Suppose I have defined a global variable global_Test in a C file (e.g., unsigned long global_Test; in one of the C modules). In a standalone assembler .S module (important note: that is really an uppercase S, not a lowercase s), I can access the variable by the following method:

.pushsection .MySection, "ax", @progbits
.org 0x00000000

/* Define the function with appropriate scope */
.global MyFunc
.type MyFunc, @function
.balign 4

MyFunc:

/* Some code ... */

/* Load the variable address into R8 */
LDDPC R8, global_Test_Addr

/* Do something with the variable, e.g., set it to 1 */
MOV   R9, 1
ST.W  R8, R9

/* Further code ... */
ret

.type global_Test_Addr, @function
.balign 4
global_Test_Addr:
  .long global_Test

This method works reliably, but as mentioned above, it seems dumb to me. It is clear that the address of the variable is known at compile time or link time, respectively, but I didn't find a way to use it in another way than shown above. For example, the following does not work:

.pushsection .MySection, "ax", @progbits
.org 0x00000000

/* Define the function with appropriate scope */
.global MyFunc
.type MyFunc, @function
.balign 4

MyFunc:

/* Some code ... */

/* Load the variable address into R8 */
MOV R8, global_Test & 0x0000FFFF
ORH R8, global_Test >> 16

/* Further code ... */
ret

With the above code, the assembler throws errors like

error: invalid operands (*UND* and *ABS* sections) for `&'
error: invalid operands (*UND* and *ABS* sections) for `>>'

In case somebody wonders: Loading a register like R8 in two steps is necessary because the AVR32 instruction set does not feature an instruction that loads a 32-bit immediate into a register at once.

I am aware that the MCU mentioned above has only 64 kB of internal RAM, and hence I simply could use MOV R8, global_Test, which works and does not lead to error messages.

However, I am interested in general why something simple like MOV R8, global_Test >> 16 leads to errors, especially taking into account that e.g. MOV R8, 123456789 >> 16 does not lead to errors. I don't get why the constant 123456789 is treated differently from the constant global_Test, which (in assembler code) is the address of global_Test and is evaluated at compile or linkage time.

Update / Additional information

I should have mentioned that I believe that the toolchain is constructed in a way that I wouldn't have expected. It seems that the assembler modules are not translated by a special assembler like gas, but by the normal avr32-gcc that also translates the C modules. That is, the (preprocessing) assembler is the same executable as the C compiler. When building the project, I see the following line in the output:

Invoking: AVR32/GNU Preprocessing Assembler : 4.4.7
"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr32\avr32-gnu-toolchain\bin\avr32-gcc.exe" -x assembler-with-cpp -c -mpart=uc3c0512c -I "C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\UC3C_DFP\1.0.49\include\AT32UC3C0512C" -I "../source" -I "C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\UC3C_DFP\1.0.77\include\AT32UC3C0512C"  -MD -MP -MF "core/slave/tc-s.d" -MT"core/slave/tc-s.d" -MT"core/slave/tc-s.o" -Wa,-g   -o "core/slave/tc-s.o" "../core/slave/tc-s.S" 

I vaguely remember that I can invoke a different assembler if I change the file extension for the assembler module from an uppercase S to a lowercase s, but this didn't work a few moments ago (the same compiler / assembler was invoked). I'll have to investigate this.


Solution

  • you cannot use C-style bitwise operators (&, >>) on symbols like global_Test, because those are relocatable.

    You must use GAS relocation modifiers that tell the assembler to take the low or high 16 bits of the address.

    EDIT: tested and lo() hi() is the correct syntax

        mov  r8, lo(global_Test)
        orh  r8, hi(global_Test)
    

    lo(global_Test) tells the assembler: Insert a relocation for the lower 16 bits of global_Test’s address.

    hi(global_Test) tells it: Insert a relocation for the upper 16 bits of global_Test’s address.

    The linker will then fix these correctly when the final address is known.

    EDIT:

    Compile using -Os -S

    unsigned x;
    
    
    void foo(void)
    {
        unsigned *volatile p = &x;
    }
    

    and see the asembler output. I do not have avr32 toolchain but it can be lo16(global_Test) or lo(global_Test)