/** * \file * * * \brief CPU-specific stack frame handling macros. * * These are mainly used by the portable part of the scheduler * to work with the process stack frames. * * \author Giovanni Bajo * \author Bernie Innocenti * \author Stefano Fedrigo * \author Francesco Sacchi */ #ifndef CPU_FRAME_H #define CPU_FRAME_H #include #include "cfg/cfg_arch.h" /* ARCH_EMUL */ #include /* for uintXX_t */ #if CPU_X86 #if CPU_X86_32 #define CPU_SAVED_REGS_CNT 2 #elif CPU_X86_64 #define CPU_SAVED_REGS_CNT 8 #else #error "unknown CPU" #endif #define CPU_STACK_GROWS_UPWARD 0 #define CPU_SP_ON_EMPTY_SLOT 0 #elif CPU_ARM #define CPU_SAVED_REGS_CNT 8 #define CPU_STACK_GROWS_UPWARD 0 #define CPU_SP_ON_EMPTY_SLOT 0 #elif CPU_CM3 #define CPU_SAVED_REGS_CNT 8 #define CPU_STACK_GROWS_UPWARD 0 #define CPU_SP_ON_EMPTY_SLOT 0 #elif CPU_PPC #define CPU_SAVED_REGS_CNT 1 #define CPU_STACK_GROWS_UPWARD 0 #define CPU_SP_ON_EMPTY_SLOT 1 #elif CPU_DSP56K #define CPU_SAVED_REGS_CNT 8 #define CPU_STACK_GROWS_UPWARD 1 #define CPU_SP_ON_EMPTY_SLOT 0 #elif CPU_AVR #define CPU_SAVED_REGS_CNT 18 #define CPU_STACK_GROWS_UPWARD 0 #define CPU_SP_ON_EMPTY_SLOT 1 #elif CPU_MSP430 #define CPU_SAVED_REGS_CNT 16 #define CPU_STACK_GROWS_UPWARD 1 #define CPU_SP_ON_EMPTY_SLOT 0 #else #error No CPU_... defined. #endif #ifndef CPU_STACK_GROWS_UPWARD #error CPU_STACK_GROWS_UPWARD should have been defined to either 0 or 1 #endif #ifndef CPU_SP_ON_EMPTY_SLOT #error CPU_SP_ON_EMPTY_SLOT should have been defined to either 0 or 1 #endif /// Default for macro not defined in the right arch section #ifndef CPU_REG_INIT_VALUE #define CPU_REG_INIT_VALUE(reg) (reg) #endif /* * Support stack handling peculiarities of a few CPUs. * * Most processors let their stack grow downward and * keep SP pointing at the last pushed value. */ #if !CPU_STACK_GROWS_UPWARD #if !CPU_SP_ON_EMPTY_SLOT /* Most microprocessors (x86, m68k...) */ #define CPU_PUSH_WORD(sp, data) \ do { *--(sp) = (data); } while (0) #define CPU_POP_WORD(sp) \ (*(sp)++) #else /* AVR insanity */ #define CPU_PUSH_WORD(sp, data) \ do { *(sp)-- = (data); } while (0) #define CPU_POP_WORD(sp) \ (*++(sp)) #endif #else /* CPU_STACK_GROWS_UPWARD */ #if !CPU_SP_ON_EMPTY_SLOT /* DSP56K and other weirdos */ #define CPU_PUSH_WORD(sp, data) \ do { *++(sp) = (cpu_stack_t)(data); } while (0) #define CPU_POP_WORD(sp) \ (*(sp)--) #else #error I bet you cannot find a CPU like this #endif #endif #if CPU_DSP56K /* * DSP56k pushes both PC and SR to the stack in the JSR instruction, but * RTS discards SR while returning (it does not restore it). So we push * 0 to fake the same context. */ #define CPU_PUSH_CALL_FRAME(sp, func) \ do { \ CPU_PUSH_WORD((sp), (func)); \ CPU_PUSH_WORD((sp), 0x100); \ } while (0); #elif CPU_CM3 #if CONFIG_KERN_PREEMPT INLINE void cm3_preempt_switch_context(cpu_stack_t **new_sp, cpu_stack_t **old_sp) { register cpu_stack_t **__new_sp asm ("r0") = new_sp; register cpu_stack_t **__old_sp asm ("r1") = old_sp; asm volatile ("svc #0" : : "r"(__new_sp), "r"(__old_sp) : "memory", "cc"); } #define asm_switch_context cm3_preempt_switch_context #define CPU_CREATE_NEW_STACK(stack) \ do { \ size_t i; \ /* Initialize process stack frame */ \ CPU_PUSH_WORD((stack), 0x01000000); /* xPSR */ \ CPU_PUSH_WORD((stack), (cpu_stack_t)proc_entry); /* pc */ \ CPU_PUSH_WORD((stack), 0); /* lr */ \ CPU_PUSH_WORD((stack), 0); /* ip */ \ CPU_PUSH_WORD((stack), 0); /* r3 */ \ CPU_PUSH_WORD((stack), 0); /* r2 */ \ CPU_PUSH_WORD((stack), 0); /* r1 */ \ CPU_PUSH_WORD((stack), 0); /* r0 */ \ CPU_PUSH_WORD((stack), 0xfffffffd); /* lr_exc */ \ /* Push a clean set of CPU registers for asm_switch_context() */ \ for (i = 0; i < CPU_SAVED_REGS_CNT; i++) \ CPU_PUSH_WORD(stack, CPU_REG_INIT_VALUE(i)); \ CPU_PUSH_WORD(stack, IRQ_PRIO_DISABLED); \ } while (0) #endif /* CONFIG_KERN_PREEMPT */ #elif CPU_AVR /* * On AVR, addresses are pushed into the stack as little-endian, while * memory accesses are big-endian (actually, it's a 8-bit CPU, so there is * no natural endianess). */ #define CPU_PUSH_CALL_FRAME(sp, func) \ do { \ uint16_t funcaddr = (uint16_t)(func); \ CPU_PUSH_WORD((sp), funcaddr); \ CPU_PUSH_WORD((sp), funcaddr>>8); \ } while (0) /* * If the kernel is in idle-spinning, the processor executes: * * IRQ_ENABLE; * CPU_IDLE; * IRQ_DISABLE; * * IRQ_ENABLE is translated in asm as "sei" and IRQ_DISABLE as "cli". * We could define CPU_IDLE to expand to none, so the resulting * asm code would be: * * sei; * cli; * * But Atmel datasheet states: * "When using the SEI instruction to enable interrupts, * the instruction following SEI will be executed *before* * any pending interrupts", so "cli" is executed before any * pending interrupt with the result that IRQs will *NOT* * be enabled! * To ensure that IRQ will run a NOP is required. */ #define CPU_IDLE NOP #elif CPU_PPC #define CPU_PUSH_CALL_FRAME(sp, func) \ do { \ CPU_PUSH_WORD((sp), (cpu_stack_t)(func)); /* LR -> 8(SP) */ \ CPU_PUSH_WORD((sp), 0); /* CR -> 4(SP) */ \ } while (0) #endif #ifndef CPU_PUSH_CALL_FRAME #define CPU_PUSH_CALL_FRAME(sp, func) \ CPU_PUSH_WORD((sp), (cpu_stack_t)(func)) #endif /** * \def CPU_IDLE * * \brief Invoked by the scheduler to stop the CPU when idle. * * This hook can be redefined to put the CPU in low-power mode, or to * profile system load with an external strobe, or to save CPU cycles * in hosted environments such as emulators. */ #ifndef CPU_IDLE #define CPU_IDLE PAUSE #endif /* !CPU_IDLE */ /** * Default macro for creating a new Process stack */ #ifndef CPU_CREATE_NEW_STACK #define CPU_CREATE_NEW_STACK(stack) \ do { \ size_t i; \ /* Initialize process stack frame */ \ CPU_PUSH_CALL_FRAME(stack, proc_entry); \ /* Push a clean set of CPU registers for asm_switch_context() */ \ for (i = 0; i < CPU_SAVED_REGS_CNT; i++) \ CPU_PUSH_WORD(stack, CPU_REG_INIT_VALUE(i)); \ } while (0) #endif #endif /* CPU_ATTR_H */