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

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

ISR Function prologues on AVR with Binutils and LD


From: Ian Molton
Subject: ISR Function prologues on AVR with Binutils and LD
Date: Wed, 15 Dec 2021 19:25:26 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.3.1

Hi,

I've dug about in the sourcecode of gcc and gas, in order to understand
how it works.

in particular, in gas

avr_patch_gccisr_frag (fragS *fr, int reg)

There is behaviour, specific to AVR that allows binutils to analyse the
compiler output for interrupt routines, and causes it to generate
special prologue/epilogues that preserve the CPU state.

My problem is that these appear to have been designed with a particular
approach/model for interrupt handling in mind, and are proving to be
frustrating now that I wish to write my own kernel for AVR. In
particular, I want to call schedule() following an interrupt handler
(and prior to returning to a thread) that causes schedule() to be
required, which would in turn require saving the entire current thread
state, but if I don't call it, then I want to avoid pushing a bunch of
un-necessary regs.

For example

void __attribute__((signal)) __vector_20(void)
{
        char a;

        a++

        return;
}

As it stands, the code generated by gcc, I assume, looks something like:


.foo
        gccisr 0

        add rX ...

        gccisr 1
        gccisr 2


Which would compile into something like: (in its "fuller form")

.foo
        push    r1
        push    r0
        in      r0, 0x3f        ; 63
        push    r0
        eor     r1, r1
        push rX

        add rx ...

        pop rX
        pop r1
        pop r0

It seems that it even checks called functions, so its ideal for writing
interrupt handlers.

However, I want to use a separate stack for IRQs, and this prevents me
doing so, AFAICT.

I could have my interrupt call an assembler stub beforehand to switch
SP, but to do so, I must save a couple of registers to the stack. I
don't want to save them *again* in the prologue, as its wasteful and on
a hot path.

I could write code to switch stack within foo(), but this won't work,
since the gccisr prologue will come before it, which is not right either.

I can see no other gcc / binutils option that would allow me to achieve
this trick - I had hoped that I could use some sort of function
prologue, but I can't see how, and it would suffer the same problems as
above.

I can see that I can trivially modify binutils so that it *NEVER* emits
the code to save SREG, only the registers to push. This gets be half-way
there.

What I need then, is a way to prepend my ISR "prologue" to foo()

In a simplistic way, I could call an assembler shim, eg.

.call_foo
        <my ISR prologue to save SREG>
        <switch stack>
        rjmp foo()

which would clearly work, but the rjmp could be avoided if I can place
the code of .call_foo immediately prior to the code of foo(). I assume
the linker can be instructed to do this, but I'm at a bit of a loss as
to how right now.

It seems like being able to disable the "extra prologue" and only have
gas fix up the stack, would be a useful option.

Documentation is sparse in this area, and if gcc has a more generic way
of doing this or if I'm barking up the wrong tree, please do mention...

-Ian



reply via email to

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