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:
- The value of the variable
a
is loaded from RAM into a register (let it ber16
). - The value of
r16
register is compared with zero. - While comparing the value of the Status register is changing. Some bits in this register reflect the comparision result.
- 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:
a
is loaded from RAM intor16
register.r16
is compared with zero.- 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 - Branch instruction examine Status register (from step 3).
Everithing works well.
B. Naked interrupt execution
Main program starts execution of if (a == 0)
condition:
a
is loaded from RAM intor16
register.r16
is compared with zero.- 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 - 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.