Undefined reference (pre-pro symbols in asm())

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

Hello,

I am working with Atmel Studio 7.0.2389 and am hanging on a really stupid problem. The project is with an Xmega32e5 and I am using inline assembler in some functions. Out of some strange reason I get the "undefined reference" error on the NVM defines. I have the #include <avr/io.h> added. When I do a goto definition it finds the defines. 

 

this is just examplary and also produces the error "undeines reference to 'NVM_CMD'": 

 

#include <avr/io.h>

 

void write_flash(uint16_t adr, uint8_t *data)
{
    _adr = adr;
    _dadr = (uint16_t)&data;
    asm volatile(
      "sts NVM_CMD, r1                  \n"
    );
}

 

I could not figure out what the problem is. 

 

Thanks

 

Last Edited: Fri. Nov 22, 2019 - 10:43 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

You have mis-understoof how to use asm() - it's actually far more complex than you think. Remember how a C compiler actually works...

 

You write C, you may well litter it full of #include's and #define's which are pre-processor macros.

 

You "compile" this but what that really means is the foo.c file is first passed to the C pre-processor. It reads things like #include/#define/#if/etc and acts upon them. Some #defines may be multiply nested - it can cope with finding and expanding out all of them. It writes what it comes up with to a foo.i file (i = intermediate). A "symbol" such as "NVM_CMD" might turn from:

	NVM_CMD = 0x5A;

to:

(*(volatile uint8_t *)(0x01CA)) = 0x5A;

so "NVM_CMD" really means 0x1CA with some sugar on top.

 

The file now passes to the C compiler itself. That simply reads raw C sequences from the .i file and generates Asm sequences from them. It writes the Asm out to a .s file. In the special situation when it comes across asm("...code ...") within the C it pretty much just lifts all the text in the parenthesis and dumps it verbatim into the .s file. What is within "..." is not subject to C preprocessing.

 

Finally the .s file is passed to the Assembler which turns the directives and mnemonics it finds there into opcodes. If the assembler comes across text sequences it does not recognize like "NVM_CMD" then it will assume this is a reference to an external symbol and that somewhere else there's likely to be Asm source that assigns the label NVM_CMD to some .data or .bss (or whatever) location. So in the file it outputs foo.o (where o = "object") it will put a "fixup marker" that says "I came across something called NVM_CMD, as yet I don't know what that is so can you fill it in later ("fix it up") when you find the other file where it is defined.

 

This .o file and any others from other compilations are now all presented to the linker to join (link) them together. It takes in all the .o files, pools all the data and all the symbol information and then makes sure it has everything to write out definitions of .text, .data, .bss in the final foo.elf output file.

 

If during that linking process it finds one file (foo.o) makes a fix up reference to something called NVM_CMD and in all the other .o files presented at the same time it cannot find anything to say "NVM_CMD means this" then it outputs the message "undefined reference to NVM_CMD".

 

All this started to go wrong when you used NVM_CMD within the body of asm("...code.."). The correct way to do it as shown in the asm cook book:

 

https://www.nongnu.org/avr-libc/user-manual/inline_asm.html

 

is some horrendously tortuous syntax like:

asm volatile("in %0,%1" "\n\t"
            "out %1, %2" "\n\t"
            : "=&r" (input)
            : "I" (_SFR_IO_ADDR(port)), "r" (output)
);

now, while STS in your code suggests "NVM_CMD" is _SFR_MEM_ADDR() not _SFR_IO_ADDR() the fact is that you have to use something like the above to pass the NVM_CMD symbol name (really the numeric value 0x1CA after preprocessing) across the border from the C to and into the Asm. Within the Asm  the %1 (in this example) will then be replaced with the value (0x1CA) so all the assembler ever sees is "sts 0x1CA, r1"

 

The difference is that the bits outside of "" in asm() are processed by the C preprocessor when this code is built. So if you used something like:

            : "I" (_SFR_MEM_ADDR(NVM_CMD)), "r" (output)

then this part of the asm() statement will be pre-processed and all that NVM_CMD stuff will just result into 0x1CA being passed to asm() to replace %1 within the ".. code .." string.

 

Personally I think this is detestable. It's like learning a whole new language! The very simplest solution would have been to forget asm() all together as it seems to me you are just trying to write:

NVM_CMD = 0;

so do just that and the C compiler (and preprocessor) will convert this to "STS 0x1CA, r1" anyway!. If there is some special time dependent sequence of opcodes that must be performed and it HAS to be in Asm then my next choice would be to put the whole implementation of write_flash() in a .S file as a callable Asm function.

 

Note that the avr-gcc compiler driver makes a distinction between foo.s and foo.S. If you put Asm in foo.s and ask to build it then it goes straight to the assembler so there's no chance of any pre-pro macros being expanded. If the file extension is upper case S not lower-case S then it is treated differently. Such a file is passed first to the C prepprocessor (so #include, #define and so on are processed) then a .i file that creates is passed to the assembler.

 

So if you do use separate Asm source files then, unless you have a string reason otherwise, always make them .S not .s files.

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

I never understand why people have this desire to sprinkle raw assembler in their 'C' source files?!

 

surprise

 

Inline assembler is always fraught with gotchas, caveats, and catches - in any toolchain.

 

Most uses of inline assembler seem misguided, at best - so I thoroughly agree with:

 

clawson wrote:
forget asm() all together

The 'C' compiler can generally do it perfectly well from plain, standard 'C' source!

 

Otherwise, if there really is some truly compelling reason to have to use assembler:

put the whole implementation of write_flash() in a .S file as a callable Asm function.

Which leaves your 'C' nice & clean (and, potentially, portable), and keeps the "tricky" stuff where it belongs in a separate Assembler file.

 

Having it in a separate Assembler file highlights the fact that it is "special".

 

This applies generally - not just to AVR.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

thanks for the replies.

 

@clawson: thanks a lot for the very detailed and informative post. It was very helpfull for any matter as also for my question. 

 

I guess I will go for the .S solution. Trying to be lazy and "just quickly" insert some asm seems like most "just quickly" solutioins not to work out. 

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

but why not go for clawson's first suggestion - just do it in 'C' ?!

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

not possible for timing and other reasons. I need direct access to some registers like the Z-pointer. As stated, the code example was just an example and not the actual code. 

 

I have now put all in an .S file with all the stuff necessary and an #include <avr/io.h> at the top. But I still get the same error "undefined reference" to some of the symbols (like "CCP_SPM_gc"). Strangely some of the references work. If I remove the #include all of the symbols give the error. 

I do not understand how that can be. Its all in the same file.

 

Regards

 

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

You can't use _gc's in Asm. They are done as C enums. The assembler does not speak C.

 

Notice in the ioXXXX.h how half the file is #if ASM ? Half of it (plain #define's and so on) is C+Asm and half of it is C/C++ only (structs, unions, enums, typedefs etc). It's also why you get a choice between stuff like PORTB.OUTSET and PORTB_OUTSET. The former is a C/C++ concept only, the latter (a plain #define) is everything.

Last Edited: Fri. Nov 22, 2019 - 09:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, thank you. Its working now.

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

Nickelgrass wrote:
 Its working now.

Jolly good!

 

Now see Tip #5.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...