Getting working GCC inline assembly into a macro

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

Posting problem, more to come...

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

The percent signs were replaced with the ^ character.

asm volatile ( /* _mob->message_id = (CANIDT >> 21); */
    "lds ^B[msg_id], ^C[canidt]" "\n\t"
    "lds ^A[msg_id], ^D[canidt]" "\n\t"
    "andi ^B[msg_id], 0xE0" "\n\t"
    "lsl ^B[msg_id]" "\n\t"
    "rol ^A[msg_id]" "\n\t"
    "rol ^B[msg_id]" "\n\t"
    "rol ^A[msg_id]" "\n\t"
    "rol ^B[msg_id]" "\n\t"
    "rol ^A[msg_id]" "\n\t"
    "rol ^B[msg_id]" "\n\t"
    "mov ^C[msg_id], __zero_reg__" "\n\t"
    "mov ^D[msg_id], __zero_reg__" "\n\t"
: [msg_id] "=a" (_mob->message_id) : [canidt] "n" (_SFR_MEM_ADDR(CANIDT)) );

I would like to put this already working inline assembly code into a __asm__ __volatile__ GCC macro. I don't need to keep the msg_id and canidt naming in the macro. All I need is a macro with two general purpose arguments for the "=a" and "n" constraints. I have followed all the explanations/examples/documentation I could find and every time it compiles my macro argument names as new undeclared C variables. Could someone please show me a working GCC inline assembly macro example and the corresponding macro use example with the same argument constraints as used in this inline assembly. The code works, I just cannot get any variation of the macro arguments to compile without errors. I got 32 matches on the GCC forum search, but didn't find an answer.

The _mob->message_id variable and the four CANIDT registers are 32 bits.

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

For a macro shouldn't this be starting with a #define foo(a,b) of some sort?

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

Correct. The code is working inline assembly code (except for the percent sign AVRfreaks posting replacement), not a macro. I have failed miserably to get this working code into a working macro. I wouldn't know which version of all the variations/attempts to post, so I figured someone that knows what they are doing wouldn't benefit from showing my failed macro examples. It should be a simple straight forward process to get the arguments to pass to a macro, but.....

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

At the bottom of this page there are examples for both C stubs and Assembler macros. http://www.cs.mun.ca/~paul/cs4723/material/atmel/avr-libc-user-manual-1.6.5/inline_asm.html

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

It also has a section on C stub functions, which I would think would be a better way of doing it. Make the stub function "static inline" and you avoid the function call overhead (and still avoid the hassles and dangers of a macro).

Regards,
Steve A.

The Board helps those that help themselves.

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

Been there done that. I did notice the define eol continuation backwards slashes were not continued through one example macro argument list and completely missing in another example. Even changing that didn't help when rewriting it for my inline code. Although the obvious example syntax errors raises the question if these are really working examples or not. I used Google to dredge up several other documentation resources that also were not helpful for the macro problem. This one:
http://www.ibiblio.org/gferg/ldp...
enlightened me to the "n" constraint which is missing from the other cookbook document. All the documentation I have found is sparse, very abbreviated and much of it is not specific to WINAVR (for inline assembly macros). The WINAVR specific stuff like the cookbook you found with what appears to be errors and omissions is suspect. The official GCC documentation told me everything I didn't need to know about compilation options/settings, but I didn't find any useful inline assembly information.

Edit:
Using a C stub call would defeat some of the cycles that are saved by the inline assembly. I only want to use micros to pretty up the source code, which gets messy when filled up with multiple instances of duplicate or almost duplicate (sometimes with different arguments) inline code.

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

How about showing an example macro you tried that doesn't work.

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

The point is I have not found any working examples of how to do this. I certainly proved I don't know. I'm not asking anyone to rewrite my already working code into a macro, just a correct example of an inline assembly macro define and its use, one that has the same constraints as my working inline assembly code example.

Just writing the original code showed me how critical the constraints are. The cookbook uses "I" (_SFR_IO_ADDR(port)) for its examples. First I had to find _SFR_MEM_ADDR to get a usable CANIDT value that wasn't 0x20 too low, that was real easy. But the "I" constraint works with IN/OUT register addresses, not these LDS/STS register addresses. I was stuck and couldn't get it to compile at all until I found additional documentation that revealed the "n" constraint. I don't know what the problem is, but it could be allot of things, it could just be me. Specific WINAVR inline assembly isn't that hard, except for the poor quality scattered documentation.

Does anyone know the correct way to do this? A simple working example?

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

Quote:
Using a C stub call would defeat some of the cycles that are saved by the inline assembly.
If the C Stub is declared static inline, what cycles would not be saved?

Regards,
Steve A.

The Board helps those that help themselves.

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

Can you try this using:

_mob->message_id = MyMacro;
#define MyMacro  ( {uint32_t tmp; asm volatile ( /* _mob->message_id = (CANIDT >> 21); */ \
    "lds ^B[msg_id], ^C[canidt]" "\n\t" \
    "lds ^A[msg_id], ^D[canidt]" "\n\t" \
    "andi ^B[msg_id], 0xE0" "\n\t"  \
    "lsl ^B[msg_id]" "\n\t" \
    "rol ^A[msg_id]" "\n\t" \
    "rol ^B[msg_id]" "\n\t" \
    "rol ^A[msg_id]" "\n\t" \
    "rol ^B[msg_id]" "\n\t" \
    "rol ^A[msg_id]" "\n\t" \
    "rol ^B[msg_id]" "\n\t" \
    "mov ^C[msg_id], __zero_reg__" "\n\t" \
    "mov ^D[msg_id], __zero_reg__" "\n\t" \
    : [msg_id] "=a" (tmp) \
	: [canidt] "n" (_SFR_MEM_ADDR(CANIDT))); tmp;})
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK. Here's an example-

#define MYMACRO(val1,val2) \
__asm__ __volatile__( \
"lds %B0,%C1" "\n\t" \
"rol %B0" "\n\t" \
"sts %A1,%C0" "\n\t" \
: "=a" (val1) : "n" (val2) \
)

uint32_t test;
MYMACRO(test,&PORTB);

It works. If you look at the lss listing, the right offsets are being used (ABC). I just threw those in for kicks. I don't know what the a/n constraints are, but in this case, no complaints.

Also notice &PORTB is a mem address in c, which is what is needed for the lds/sts.

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

curtvm,

Thank you very much. I found the problem.

This doesn't work:
    MYMACRO (val1,val2)

This works:
    MYMACRO(val1,val2)

The big difference is without thinking I put a space between the macro name and the opening parenthesis. The compiler totally chokes and errors out on the version with the space. Of course the compiler error messages give no obvious clue to the real problem. BTW, how did you get those percent signs in your post?

CirMicro,

Thank you for your help. Your example worked. It threw me off because the inline assembly has its own output "=a", so I thought the "equal" MyMacro invocation would cause a duplicate save to _mob->message_id. However this did not happen (dang smart compiler).

Steve,

Thank you for your help. I assumed the C stub would emulate a C calling function. I was wrong, but now I fail to see any functional generated code difference between the macro and C stub, aside from source syntax and code structure differences.

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

Quote:
BTW, how did you get those percent signs in your post?
Only a % followed immediately by a number is disallowed.
Quote:
but now I fail to see any functional generated code difference between the macro and C stub
Which is the point. You can use real C to get the same result without having to do all the BS of macros.

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:

Only a % followed immediately by a number is disallowed.

Umm no, the problem is usually other way round

printf("%3d");

will get through but:

printf("#d");

won't.

From the evidence above it looks like a percent followed by a capital letter may be OK too:

printf("%D3");

Ah no, it's more subtle than that "#D" didn't work but "#D3" did.

I think it allowed D3 because it looks like a hex number?

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

And as an additional side note: you can always use the HTML entity to generate the percent sign:

printf("%d");

Stefan Ernst

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

Koshchi wrote:
Only a % followed immediately by a number is disallowed.
I was getting constant HTTP port 80 server errors until after I removed the percent signs. Maybe the server was just having a bad day?
Koshchi wrote:
Which is the point. You can use real C to get the same result without having to do all the BS of macros.
Personally being crazy enough to do GCC inline assembly in the first place :), if I start worrying about arcane macro syntax I had better go do something else other than microprocessors :lol:.

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

Mike,

Yesterday I posted my inline assembler macros (asm.h) here. I think they make using inline assembler more comfortable.

Regards
Sebastian

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

Here is my final version of the macros:

#define RIGHT_SHIFT_21(val_32, port_32) /* message_id = (CANIDT >> 21); */ \
    __asm__ __volatile__(               \
    "lds ^B[message], ^C[canid]" "\n\t" \
    "lds ^A[message], ^D[canid]" "\n\t" \
    "andi ^B[message], 0xE0" "\n\t"     \
    "lsl ^B[message]" "\n\t"            \
    "rol ^A[message]" "\n\t"            \
    "rol ^B[message]" "\n\t"            \
    "rol ^A[message]" "\n\t"            \
    "rol ^B[message]" "\n\t"            \
    "rol ^A[message]" "\n\t"            \
    "rol ^B[message]" "\n\t"            \
    "clr ^C[message]" "\n\t" /* 32 bit specific */ \
    "clr ^D[message]" "\n\t" /* 32 bit specific */ \
: [message] "=a" (val_32)               \
: [canid] "i" (_SFR_MEM_ADDR(port_32))  \
)

RIGHT_SHIFT_21 (message_id, CANIDT);
RIGHT_SHIFT_21 (message_msk, CANIDM);

#define LEFT_SHIFT_21(port_32, val_16_32) /* CANIDT = (message_id << 21); */ \
    __asm__ __volatile__(                \
    "sts ^A[canid], __zero_reg__" "\n\t" \
    "sts ^B[canid], __zero_reg__" "\n\t" \
    "lsr ^B[message]" "\n\t"             \
    "ror ^A[message]" "\n\t"             \
    "ror ^B[message]" "\n\t"             \
    "ror ^A[message]" "\n\t"             \
    "ror ^B[message]" "\n\t"             \
    "ror ^A[message]" "\n\t"             \
    "ror ^B[message]" "\n\t"             \
    "andi ^B[message], 0xE0" "\n\t"      \
    "sts ^C[canid], ^B[message]" "\n\t"  \
    "sts ^D[canid], ^A[message]" "\n\t"  \
: /* built-in port_32 output */          \
: [canid] "i" (_SFR_MEM_ADDR(port_32)), [message] "a" (val_16_32) \
)

LEFT_SHIFT_21 (CANIDT, message_id);
LEFT_SHIFT_21 (CANIDM, message_msk);

These are used with the AVR CAN chips.

Once again I cannot post with percent signs. I get a "bad request" error from the server every time.

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

Hi
in this part of "Inline Assembler Cookbook " ,said:

Quote:

In order to reuse your assembler language parts, it is useful to define them as macros and put them into include files. AVR Libc comes with a bunch of them, which could be found in the directory avr/include. Using such include files may produce compiler warnings, if they are used in modules, which are compiled in strict ANSI mode.

To avoid that, you can write __asm__ instead of asm and __volatile__ instead of volatile. These are equivalent aliases.

but when i use asm volatile , i can compile my code with any warning , why?
i wrote my macro in a header file then include in main file , my header is :

#ifndef _ASMMACRO_H
#define _ASMMACRO_H
			
#define loop_until_bit_is_clear_mojtaba(port,bit)  \
        asm volatile (             \
        "L_per=: " "sbic per0, per1" "\n\t"      \
                 "rjmp L_per="               \
                 :         					\
                 : "I" (_SFR_IO_ADDR(port)),  \
                   "I" (bit)    \
        )
			
#endif	/* asmmacro.h */

note: i use "per" instead percent-sign becuase forum don't respon to my request !