avr-gcc-list
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [avr-gcc-list] In-line Assembler Code


From: Alistair Gadd
Subject: Re: [avr-gcc-list] In-line Assembler Code
Date: Wed, 18 Mar 2015 11:24:23 +0200
User-agent: Mozilla/5.0 (Windows NT 5.2; WOW64; rv:31.0) Gecko/20100101 Thunderbird/31.5.0

Hi Erik,

I did in fact, eventually do exactly that. But I do like your tip on running xxx.S files through cpp and I'm going to use it.

I just thought that because in-line assembly was being used by members of the community, it should be something I should have a handle on. I do have a better understanding of it now, but I didn't continue with it for more or less the same reasons that you don't use it.

I was trying to make sense of an Atmel demo and the mistake that I was making was that I didn't see that there were actually 2 instances compiled and I was only examining the first one. The following had also been used elsewhere in the project:

// Locate low level init function before RAM init (init3 section)
char __low_level_init(void) __attribute__ ((section (".init3"),naked));
char __low_level_init()
{
    clock_prescale_set(clock_div_1);
    return 1;
}

My test looked like this:

#include <avr/io.h>
#include <avr/power.h>

int main(void)
{
    DDRC = 0x80;
    PORTC |= 0x80;
    clock_prescale_set(clock_div_2);
    PORTC &= 0x7F;
    while(1) {};
}

and an extract from "avr/power.h" (shortened for explanation here) is:

typedef enum
{
    clock_div_1 = 0, clock_div_2 = 1, clock_div_4 = 2, clock_div_8 = 3,
clock_div_16 = 4,
    clock_div_32 = 5, clock_div_64 = 6, clock_div_128 = 7, clock_div_256 = 8
}  clock_div_t;

static __inline__   void clock_prescale_set(clock_div_t)   __attribute__((__always_inline__));
void clock_prescale_set(clock_div_t __x)
{
    uint8_t __tmp = _BV(CLKPCE);
    __asm__   __volatile__ (
    "in __tmp_reg__,__SREG__" "\n\t"
    "cli" "\n\t"
    "sts %1, %0" "\n\t"
    "sts %1, %2" "\n\t"
    "out __SREG__, __tmp_reg__"
    : /* no outputs */
    : "d" (__tmp),
    "M" (_SFR_MEM_ADDR(CLKPR)),
    "d" (__x)
    : "r0");
}

The compilations are flawless whether optimised or not, and needless to say, I felt a bit stupid when I found it.

Thanks to you Erik, Johann and Jan for the help, and I apologise for any wild goose chases.

Best regards,
Alistair.

On 2015/03/18 09:07 AM, Erik Christiansen wrote:
On 07.03.15 17:07, Alistair Gadd wrote:
I'm used to using separate assembler files and I'm not a great boffin
on using in-line assembly, but I really think I need to get a collar
on it.
Alistair, is there any reason for not using separate assembler files, as
is your custom? As clock_prescale_set() is entirely assembler, it is
ripe for linking in from a separate assembler source file, either put
through the assembler only, or avr-gcc, as preferred.

In the latest makefile I wrote for use on avr, I find I'm using:

%.o: %.c 
   $(CC) -c $(CFLAGS) -o $(OBJDIR)/$@ $<

%.o: %.s 
   $(AS) -I$(INC_DIR) $(ASFLAGS) -o $(OBJDIR)/$@ $<

%.o: %.S 
   $(CC) -c -I$(INC_DIR) -x assembler-with-cpp $(CFLAGS) -Wa,-alms=$(OBJDIR)/address@hidden -o $(OBJDIR)/$@ $<

I.e. xxx.S goes through cpp, so lots of good macros are expanded.

If register allocation is a doubt, the ABI is documented here:
http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage

I would certainly never use in-line assembler for anything other than a
desperate hack of a large C function which couldn't be fixed any other
way. In-line assembler is unreadable gibberish, and not an efficient or
adequately maintainable method for writing a complete function, IME.

Given that linking file(s) of assembler function(s) into a C program is
quite elementary, it should hardly ever be necessary to use in-line
assembler. (And in a 30 year embedded systems design/programming career,
I never found it useful to do so.)

Erik



reply via email to

[Prev in Thread] Current Thread [Next in Thread]