Home > Software design >  Why doesn't C code work properly in __attribute__((naked)) functions?
Why doesn't C code work properly in __attribute__((naked)) functions?

Time:01-21

I am working on an ARM CORTEX microcontroller and have written a function 'x' for context switching of a thread.

This function included all __asm() calls and hence I used __attribute__((naked)).

After some time, when I called a function f(), the entire function x() wasn't being called at all. Hence no context switch happened.

Why is this happening? Should I not use __attribute__((naked)) ?

Code:

In file1.c

__attribute__((naked)) void SysTick_Handler(void){

    tick_increment();

    // context switch inline assembly code
}

In file2.c

volatile uint32_t tick_cnt;
volatile uint32_t tick_freq = 1;

void tick_increment(void){

    tick_cnt  = tick_freq;

}

Since I am calling tick_increment() inside this SysTick Handler function, it never gets called when the CVR hits 0.

Note: SysTick_Handler gets called automatically when the CVR hits 0.

CodePudding user response:

The short answer is:

SysTick_Handler is an interrupt handler and has to be defined without __attribute__((naked)).

A bit longer answer:

Each interrupt routine needs to store some registers before invoking interrupt handler and restore it just before returning from interrupt. The storing and restoring procedures are generated automatically by the compiler and surround user code inside every interrupt handler. When you define interrupt handler as __attribute__((naked)) you get rid of these automatically generated code messing up the main program execution.

Example of unexpected behaviour

Let's have a look how __attribute__((naked)) interrupt handler can mess up the main program.
For example, consider one of the possible implementations of if (a == 0) condition:

  1. The value of the variable a is loaded from RAM into a register (let it be r16).
  2. The value of r16 register is compared with zero.
  3. While comparing the value of the Status register is changing. Some bits in this register reflect the comparision result.
  4. Branch instruction examine the comparison result bit of the Status register and perfirm jump to one branch or another.

The condition hereby works right only if there are no unexpected changes of Status register between steps 2 and 4.

A. Normal interrupt execution

Main program starts execution of if (a == 0) condition:

  1. a is loaded from RAM into r16 register.
  2. r16 is compared with zero.
  3. Status register updated.
    ---> INTERRUPT IS HIT
    a. Interrupt handler is executed.
    b. Save Status register to stack.
    c. Execute user code (suppose Status register is changed).
    d. Restore Status register from stack.
    e. Return from interrupt handler.
    <--- INTERRUPT COMPLETED
  4. Branch instruction examine Status register (from step 3).

Everithing works well.

B. Naked interrupt execution

Main program starts execution of if (a == 0) condition:

  1. a is loaded from RAM into r16 register.
  2. r16 is compared with zero.
  3. Status register updated.
    ---> INTERRUPT IS HIT
    a. Interrupt handler is executed.
    b. Execute user code (suppose Status register is changed).
    c. Return from interrupt handler.
    <--- INTERRUPT COMPLETED
  4. Branch instruction examine Status register (from step b).

The value of examined Status register does not reflect the comparison done in step 2, the branch instruction could jump to the wrong branch.

This is just an example, real messing up can arise in many other ways with the __attribute__((naked)) interrupt handlers.

  •  Tags:  
  • carm
  • Related