X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=bertos%2Fcpu%2Firq.h;h=c234d99efca74a20151844fa4196a9595b81936c;hb=b64d74abecd41a9ea563eb6197ace94b78eee675;hp=3a84dcb33b225c8ea5fc80d3b417d446647bbebb;hpb=5691738d997fcb88e9ff1cb905c4e180ece79867;p=bertos.git diff --git a/bertos/cpu/irq.h b/bertos/cpu/irq.h index 3a84dcb3..c234d99e 100644 --- a/bertos/cpu/irq.h +++ b/bertos/cpu/irq.h @@ -44,7 +44,10 @@ #include "detect.h" #include "types.h" +#include /* proc_needPreempt() / proc_preempt() */ + #include /* for uintXX_t */ +#include "cfg/cfg_proc.h" /* CONFIG_KERN_PREEMPT */ #if CPU_I196 #define IRQ_DISABLE disable_interrupt() @@ -60,6 +63,179 @@ #define IRQ_RESTORE(x) FIXME #endif /* OS_EMBEDDED */ +#elif CPU_CM3 + /* Cortex-M3 */ + + /* + * Interrupt priority. + * + * NOTE: 0 means that an interrupt is not affected by the global IRQ + * priority settings. + */ + #define IRQ_PRIO 0x80 + #define IRQ_PRIO_MIN 0xf0 + #define IRQ_PRIO_MAX 0 + /* + * To disable interrupts we just raise the system base priority to a + * number lower than the default IRQ priority. In this way, all the + * "normal" interrupt can't be triggered. High-priority interrupt can + * still happen (at the moment only the soft-interrupt svcall uses a + * priority greater than the default IRQ priority). + * + * To enable interrupts we set the system base priority to 0, that + * means IRQ priority mechanism is disabled, and any interrupt can + * happen. + */ + #define IRQ_PRIO_DISABLED 0x40 + #define IRQ_PRIO_ENABLED 0 + + #ifdef __IAR_SYSTEMS_ICC__ + INLINE cpu_flags_t CPU_READ_FLAGS(void) + { + return __get_BASEPRI(); + } + + INLINE void CPU_WRITE_FLAGS(cpu_flags_t flags) + { + __set_BASEPRI(flags); + } + + extern uint32_t CPU_READ_IPSR(void); + extern bool irq_running(void); + + #define IRQ_DISABLE CPU_WRITE_FLAGS(IRQ_PRIO_DISABLED) + + #define IRQ_ENABLE CPU_WRITE_FLAGS(IRQ_PRIO_ENABLED) + + #define IRQ_SAVE_DISABLE(x) \ + do { \ + x = CPU_READ_FLAGS(); \ + IRQ_DISABLE; \ + } while (0) + + #define IRQ_RESTORE(x) \ + do { \ + CPU_WRITE_FLAGS(x); \ + } while (0) + #else /* !__IAR_SYSTEMS_ICC__ */ + #define IRQ_DISABLE \ + ({ \ + register cpu_flags_t reg = IRQ_PRIO_DISABLED; \ + asm volatile ( \ + "msr basepri, %0" \ + : : "r"(reg) : "memory", "cc"); \ + }) + + #define IRQ_ENABLE \ + ({ \ + register cpu_flags_t reg = IRQ_PRIO_ENABLED; \ + asm volatile ( \ + "msr basepri, %0" \ + : : "r"(reg) : "memory", "cc"); \ + }) + + #define CPU_READ_FLAGS() \ + ({ \ + register cpu_flags_t reg; \ + asm volatile ( \ + "mrs %0, basepri" \ + : "=r"(reg) : : "memory", "cc"); \ + reg; \ + }) + + #define IRQ_SAVE_DISABLE(x) \ + ({ \ + x = CPU_READ_FLAGS(); \ + IRQ_DISABLE; \ + }) + + #define IRQ_RESTORE(x) \ + ({ \ + asm volatile ( \ + "msr basepri, %0" \ + : : "r"(x) : "memory", "cc"); \ + }) + + INLINE bool irq_running(void) + { + register uint32_t ret; + + /* + * Check if the current stack pointer is the main stack or + * process stack: we use the main stack only in Handler mode, + * so this means we're running inside an ISR. + */ + asm volatile ( + "mrs %0, msp\n\t" + "cmp sp, %0\n\t" + "ite ne\n\t" + "movne %0, #0\n\t" + "moveq %0, #1\n\t" : "=r"(ret) : : "cc"); + return ret; + } + #endif /* __IAR_SYSTEMS_ICC__ */ + + #define IRQ_ENABLED() (CPU_READ_FLAGS() == IRQ_PRIO_ENABLED) + + #define IRQ_RUNNING() irq_running() + + #if (CONFIG_KERN && CONFIG_KERN_PREEMPT) + + #define DECLARE_ISR_CONTEXT_SWITCH(func) \ + void func(void); \ + INLINE void __isr_##func(void); \ + void func(void) \ + { \ + __isr_##func(); \ + if (!proc_needPreempt()) \ + return; \ + /* + * Set a PendSV request. + * + * The preemption handler will be called immediately + * after this ISR in tail-chaining mode (without the + * overhead of hardware state saving and restoration + * between interrupts). + */ \ + HWREG(NVIC_INT_CTRL) = NVIC_INT_CTRL_PEND_SV; \ + } \ + INLINE void __isr_##func(void) + + /** + * With task priorities enabled each ISR is used a point to + * check if we need to perform a context switch. + * + * Instead, without priorities a context switch can occur only + * when the running task expires its time quantum. In this last + * case, the context switch can only occur in the timer ISR, + * that must be always declared with the + * DECLARE_ISR_CONTEXT_SWITCH() macro. + */ + #if CONFIG_KERN_PRI + #define DECLARE_ISR(func) \ + DECLARE_ISR_CONTEXT_SWITCH(func) + /** + * Interrupt service routine prototype: can be used for + * forward declarations. + */ + #define ISR_PROTO(func) \ + ISR_PROTO_CONTEXT_SWITCH(func) + #endif /* !CONFIG_KERN_PRI */ + #endif + + #ifndef ISR_PROTO + #define ISR_PROTO(func) void func(void) + #endif + #ifndef DECLARE_ISR + #define DECLARE_ISR(func) void func(void) + #endif + #ifndef DECLARE_ISR_CONTEXT_SWITCH + #define DECLARE_ISR_CONTEXT_SWITCH(func) void func(void) + #endif + #ifndef ISR_PROTO_CONTEXT_SWITCH + #define ISR_PROTO_CONTEXT_SWITCH(func) void func(void) + #endif + #elif CPU_ARM #ifdef __IAR_SYSTEMS_ICC__ @@ -68,8 +244,8 @@ #if __CPU_MODE__ == 1 /* Thumb */ /* Use stubs */ - extern cpuflags_t get_CPSR(void); - extern void set_CPSR(cpuflags_t flags); + extern cpu_flags_t get_CPSR(void); + extern void set_CPSR(cpu_flags_t flags); #else #define get_CPSR __get_CPSR #define set_CPSR __set_CPSR @@ -92,64 +268,184 @@ #define IRQ_ENABLED() \ ((bool)(get_CPSR() & 0xb0)) - #define BREAKPOINT /* asm("bkpt 0") DOES NOT WORK */ - #else /* !__IAR_SYSTEMS_ICC__ */ - #define IRQ_DISABLE \ - do { \ - asm volatile ( \ - "mrs r0, cpsr\n\t" \ - "orr r0, r0, #0xc0\n\t" \ - "msr cpsr_c, r0" \ - ::: "r0" \ - ); \ + #define IRQ_DISABLE \ + do { \ + cpu_flags_t sreg; \ + asm volatile ( \ + "mrs %0, cpsr\n\t" \ + "orr %0, %0, #0xc0\n\t" \ + "msr cpsr_c, %0\n\t" \ + : "=r" (sreg) : : "memory", "cc"); \ } while (0) - #define IRQ_ENABLE \ - do { \ - asm volatile ( \ - "mrs r0, cpsr\n\t" \ - "bic r0, r0, #0xc0\n\t" \ - "msr cpsr_c, r0" \ - ::: "r0" \ - ); \ + #define IRQ_ENABLE \ + do { \ + cpu_flags_t sreg; \ + asm volatile ( \ + "mrs %0, cpsr\n\t" \ + "bic %0, %0, #0xc0\n\t" \ + "msr cpsr_c, %0\n\t" \ + : "=r" (sreg) : : "memory", "cc"); \ } while (0) - #define IRQ_SAVE_DISABLE(x) \ - do { \ - asm volatile ( \ - "mrs %0, cpsr\n\t" \ - "orr r0, %0, #0xc0\n\t" \ - "msr cpsr_c, r0" \ - : "=r" (x) \ - : /* no inputs */ \ - : "r0" \ - ); \ + #define IRQ_SAVE_DISABLE(x) \ + do { \ + register cpu_flags_t sreg; \ + asm volatile ( \ + "mrs %0, cpsr\n\t" \ + "orr %1, %0, #0xc0\n\t" \ + "msr cpsr_c, %1\n\t" \ + : "=r" (x), "=r" (sreg) \ + : : "memory", "cc"); \ } while (0) - #define IRQ_RESTORE(x) \ - do { \ - asm volatile ( \ - "msr cpsr_c, %0" \ - : /* no outputs */ \ - : "r" (x) \ - ); \ + #define IRQ_RESTORE(x) \ + do { \ + asm volatile ( \ + "msr cpsr_c, %0\n\t" \ + : : "r" (x) : "memory", "cc"); \ } while (0) - #define CPU_READ_FLAGS() \ - ({ \ - cpuflags_t sreg; \ - asm volatile ( \ - "mrs %0, cpsr\n\t" \ - : "=r" (sreg) \ - : /* no inputs */ \ - ); \ - sreg; \ + #define CPU_READ_FLAGS() \ + ({ \ + cpu_flags_t sreg; \ + asm volatile ( \ + "mrs %0, cpsr\n\t" \ + : "=r" (sreg) : : "memory", "cc"); \ + sreg; \ }) #define IRQ_ENABLED() ((CPU_READ_FLAGS() & 0xc0) != 0xc0) + #define IRQ_RUNNING() ((CPU_READ_FLAGS() & 0x0F) == 0x02) + + #if (CONFIG_KERN && CONFIG_KERN_PREEMPT) + EXTERN_C void asm_irq_switch_context(void); + + /** + * At the beginning of any ISR immediately ajust the + * return address and store all the caller-save + * registers (the ISR may change these registers that + * are shared with the user-context). + */ + #define IRQ_ENTRY() asm volatile ( \ + "sub lr, lr, #4\n\t" \ + "stmfd sp!, {r0-r3, ip, lr}\n\t") + #define IRQ_EXIT() asm volatile ( \ + "b asm_irq_switch_context\n\t") + /** + * Function attribute to declare an interrupt service + * routine. + * + * An ISR function must be declared as naked because we + * want to add our IRQ_ENTRY() prologue and IRQ_EXIT() + * epilogue code to handle the context switch and save + * all the registers (not only the callee-save). + * + */ + #define ISR_FUNC __attribute__((naked)) + + /** + * The compiler cannot establish which + * registers actually need to be saved, because + * the interrupt can happen at any time, so the + * "normal" prologue and epilogue used for a + * generic function call are not suitable for + * the ISR. + * + * Using a naked function has the drawback that + * the stack is not automatically adjusted at + * this point, like a "normal" function call. + * + * So, an ISR can _only_ contain other function + * calls and they can't use the stack in any + * other way. + * + * NOTE: we need to explicitly disable IRQs after + * IRQ_ENTRY(), because the IRQ status flag is not + * masked by the hardware and an IRQ ack inside the ISR + * may cause the triggering of another IRQ before + * exiting from the current ISR. + * + * The respective IRQ_ENABLE is not necessary, because + * IRQs will be automatically re-enabled when restoring + * the context of the user task. + */ + #define DECLARE_ISR_CONTEXT_SWITCH(func) \ + void ISR_FUNC func(void); \ + static NOINLINE void __isr_##func(void); \ + void ISR_FUNC func(void) \ + { \ + IRQ_ENTRY(); \ + IRQ_DISABLE; \ + __isr_##func(); \ + IRQ_EXIT(); \ + } \ + static NOINLINE void __isr_##func(void) + /** + * Interrupt service routine prototype: can be used for + * forward declarations. + */ + #define ISR_PROTO_CONTEXT_SWITCH(func) \ + void ISR_FUNC func(void) + /** + * With task priorities enabled each ISR is used a point to + * check if we need to perform a context switch. + * + * Instead, without priorities a context switch can occur only + * when the running task expires its time quantum. In this last + * case, the context switch can only occur in the timer + * ISR, that must be always declared with the + * DECLARE_ISR_CONTEXT_SWITCH() macro. + */ + #if CONFIG_KERN_PRI + #define DECLARE_ISR(func) \ + DECLARE_ISR_CONTEXT_SWITCH(func) + + #define ISR_PROTO(func) \ + ISR_PROTO_CONTEXT_SWITCH(func) + #endif /* !CONFIG_KERN_PRI */ + #endif /* CONFIG_KERN_PREEMPT */ + + #ifndef ISR_FUNC + #define ISR_FUNC __attribute__((naked)) + #endif + #ifndef DECLARE_ISR + #define DECLARE_ISR(func) \ + void ISR_FUNC func(void); \ + /* \ + * FIXME: avoid the inlining of this function. \ + * \ + * This is terribly inefficient, but it's a \ + * reliable workaround to avoid gcc blowing \ + * away the stack (see the bug below): \ + * \ + * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41999 \ + */ \ + static NOINLINE void __isr_##func(void); \ + void ISR_FUNC func(void) \ + { \ + asm volatile ( \ + "sub lr, lr, #4\n\t" \ + "stmfd sp!, {r0-r3, ip, lr}\n\t"); \ + __isr_##func(); \ + asm volatile ( \ + "ldmfd sp!, {r0-r3, ip, pc}^\n\t"); \ + } \ + static NOINLINE void __isr_##func(void) + #endif + #ifndef DECLARE_ISR_CONTEXT_SWITCH + #define DECLARE_ISR_CONTEXT_SWITCH(func) DECLARE_ISR(func) + #endif + #ifndef ISR_PROTO + #define ISR_PROTO(func) void ISR_FUNC func(void) + #endif + #ifndef ISR_PROTO_CONTEXT_SWITCH + #define ISR_PROTO_CONTEXT_SWITCH(func) ISR_PROTO(func) + #endif + #endif /* !__IAR_SYSTEMS_ICC_ */ #elif CPU_PPC @@ -166,7 +462,6 @@ #elif CPU_DSP56K - #define BREAKPOINT asm(debug) #define IRQ_DISABLE do { asm(bfset #0x0200,SR); asm(nop); } while (0) #define IRQ_ENABLE do { asm(bfclr #0x0200,SR); asm(nop); } while (0) @@ -220,18 +515,62 @@ ); \ (bool)(sreg & 0x80); \ }) + #if (CONFIG_KERN && CONFIG_KERN_PREEMPT) + #define DECLARE_ISR_CONTEXT_SWITCH(vect) \ + INLINE void __isr_##vect(void); \ + ISR(vect) \ + { \ + __isr_##vect(); \ + IRQ_PREEMPT_HANDLER(); \ + } \ + INLINE void __isr_##vect(void) + + /** + * With task priorities enabled each ISR is used a point to + * check if we need to perform a context switch. + * + * Instead, without priorities a context switch can occur only + * when the running task expires its time quantum. In this last + * case, the context switch can only occur in the timer ISR, + * that must be always declared with the + * DECLARE_ISR_CONTEXT_SWITCH() macro. + */ + #if CONFIG_KERN_PRI + #define DECLARE_ISR(func) \ + DECLARE_ISR_CONTEXT_SWITCH(func) + /** + * Interrupt service routine prototype: can be used for + * forward declarations. + */ + #define ISR_PROTO(func) \ + ISR_PROTO_CONTEXT_SWITCH(func) + #endif /* !CONFIG_KERN_PRI */ + #endif + + #ifndef ISR_PROTO + #define ISR_PROTO(vect) ISR(vect) + #endif + #ifndef DECLARE_ISR + #define DECLARE_ISR(vect) ISR(vect) + #endif + #ifndef DECLARE_ISR_CONTEXT_SWITCH + #define DECLARE_ISR_CONTEXT_SWITCH(vect) ISR(vect) + #endif + #ifndef ISR_PROTO_CONTEXT_SWITCH + #define ISR_PROTO_CONTEXT_SWITCH(vect) ISR(vect) + #endif + +#elif CPU_MSP430 + + /* Get the compiler defined macros */ + #include + #define IRQ_DISABLE dint() + #define IRQ_ENABLE eint() + #else #error No CPU_... defined. #endif -#ifndef IRQ_ENTRY - #define IRQ_ENTRY() /* NOP */ -#endif - -#ifndef IRQ_EXIT - #define IRQ_EXIT() /* NOP */ -#endif - #ifdef IRQ_RUNNING /// Ensure callee is running within an interrupt #define ASSERT_IRQ_CONTEXT() ASSERT(IRQ_RUNNING()) @@ -239,6 +578,7 @@ /// Ensure callee is not running within an interrupt #define ASSERT_USER_CONTEXT() ASSERT(!IRQ_RUNNING()) #else + #define IRQ_RUNNING() false #define ASSERT_USER_CONTEXT() do {} while(0) #define ASSERT_IRQ_CONTEXT() do {} while(0) #endif @@ -254,9 +594,21 @@ #define IRQ_ASSERT_DISABLED() do {} while(0) #endif -// OBSOLETE names -#define ASSERT_IRQ_ENABLED() IRQ_ASSERT_ENABLED() -#define ASSERT_IRQ_DISABLED() IRQ_ASSERT_DISABLED() + +#ifndef IRQ_PREEMPT_HANDLER + #if (CONFIG_KERN && CONFIG_KERN_PREEMPT) + /** + * Handle preemptive context switch inside timer IRQ. + */ + INLINE void IRQ_PREEMPT_HANDLER(void) + { + if (proc_needPreempt()) + proc_preempt(); + } + #else + #define IRQ_PREEMPT_HANDLER() /* Nothing */ + #endif +#endif /** * Execute \a CODE atomically with respect to interrupts. @@ -265,16 +617,10 @@ */ #define ATOMIC(CODE) \ do { \ - cpuflags_t __flags; \ + cpu_flags_t __flags; \ IRQ_SAVE_DISABLE(__flags); \ CODE; \ IRQ_RESTORE(__flags); \ } while (0) - -#ifndef BREAKPOINT -#define BREAKPOINT /* nop */ -#endif - - #endif /* CPU_IRQ_H */