From: rasky Date: Fri, 30 Jul 2004 14:24:16 +0000 (+0000) Subject: Task switching con salvataggio perfetto stato di interrupt (SR) X-Git-Tag: 1.0.0~1151 X-Git-Url: https://codewiz.org/gitweb?a=commitdiff_plain;h=99ebec567d8485806f6ebe23b8c8a1b825d0cf4d;p=bertos.git Task switching con salvataggio perfetto stato di interrupt (SR) Kernel monitor per dump informazioni su stack dei processi git-svn-id: https://src.develer.com/svnoss/bertos/trunk@90 38d2e660-2303-0410-9eaa-f027e97ec537 --- diff --git a/config_kern.h b/config_kern.h index b7ee0327..2d99ef75 100755 --- a/config_kern.h +++ b/config_kern.h @@ -15,6 +15,10 @@ /* * $Log$ + * Revision 1.3 2004/07/30 14:24:16 rasky + * Task switching con salvataggio perfetto stato di interrupt (SR) + * Kernel monitor per dump informazioni su stack dei processi + * * Revision 1.2 2004/06/03 11:27:09 bernie * Add dual-license information. * @@ -36,6 +40,7 @@ #define CONFIG_KERN_TIMER (1 && CONFIG_KERN_SIGNALS) #define CONFIG_KERN_HEAP (0) #define CONFIG_KERN_SEMAPHORES (0 && CONFIG_KERN_SIGNALS) +#define CONFIG_KERN_MONITOR (1 && CONFIG_KERN_SCHED) /*\}*/ /* EXPERIMENTAL */ @@ -52,9 +57,11 @@ #endif /* Memory fill codes to help debugging */ -#ifdef _DEBUG - #define CONFIG_KERN_STACKFILLCODE 0xE1 - #define CONFIG_KERN_MEMFILLCODE 0xDB +#if CONFIG_KERN_MONITOR + #define CONFIG_KERN_STACKFILLCODE 0xA5A5 + #define CONFIG_KERN_MEMFILLCODE 0xDBDB #endif + + #endif /* CONFIG_KERN_H */ diff --git a/cpu.h b/cpu.h index c8a242f2..f373b7b1 100755 --- a/cpu.h +++ b/cpu.h @@ -17,6 +17,10 @@ /* * $Log$ + * Revision 1.9 2004/07/30 14:24:16 rasky + * Task switching con salvataggio perfetto stato di interrupt (SR) + * Kernel monitor per dump informazioni su stack dei processi + * * Revision 1.8 2004/07/30 14:15:53 rasky * Nuovo supporto unificato per detect della CPU * @@ -102,26 +106,12 @@ typedef unsigned int cpustack_t; #define CPU_REGS_CNT FIXME - #define CPU_SAVED_REGS_CNT 28 + #define CPU_SAVED_REGS_CNT 8 #define CPU_STACK_GROWS_UPWARD 1 #define CPU_SP_ON_EMPTY_SLOT 0 #define CPU_BYTE_ORDER CPU_BIG_ENDIAN - #undef CPU_REG_INIT_VALUE - INLINE uint16_t CPU_REG_INIT_VALUE(int reg) - { - if (reg == 14) - { - uint16_t omr_img; - asm(move OMR, omr_img); - return omr_img & (BV(3)/*EX*/ | BV(1)/*MB*/ | BV(0)/*MA*/); - } - else if (reg == 16)/*M01*/ - return 0xFFFF; - return 0; - } - -#elif defined (__AVR__) +#elif CPU_AVR #define NOP asm volatile ("nop" ::) #define DISABLE_INTS asm volatile ("cli" ::) @@ -205,10 +195,10 @@ #define CPU_PUSH_CALL_CONTEXT(sp, func) \ do { \ CPU_PUSH_WORD((sp), (func)); \ - CPU_PUSH_WORD((sp), 0); \ + CPU_PUSH_WORD((sp), 0x100); \ } while (0); -#elif defined (__AVR__) +#elif CPU_AVR /* In AVR, the 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). diff --git a/kern/proc.c b/kern/proc.c index 613cf58d..3971df00 100755 --- a/kern/proc.c +++ b/kern/proc.c @@ -17,6 +17,10 @@ /* * $Log$ + * Revision 1.6 2004/07/30 14:24:16 rasky + * Task switching con salvataggio perfetto stato di interrupt (SR) + * Kernel monitor per dump informazioni su stack dei processi + * * Revision 1.5 2004/07/14 14:18:09 rasky * Merge da SC: Rimosso timer dentro il task, che è uno spreco di memoria per troppi task * @@ -43,8 +47,11 @@ #include /* memset() */ -/* CPU dependent context switching routines */ +/*! CPU dependent context switching routines + * \note This function *MUST* preserve also the status of the interrupts. + */ extern void asm_switch_context(cpustack_t **new_sp, cpustack_t **save_sp); +extern int asm_switch_version(void); /* * The scheduer tracks ready and waiting processes @@ -56,6 +63,7 @@ extern void asm_switch_context(cpustack_t **new_sp, cpustack_t **save_sp); REGISTER Process *CurrentProcess; REGISTER List ProcReadyList; + #if CONFIG_KERN_PREEMPTIVE /* * The time sharing scheduler forces a task switch when @@ -73,11 +81,82 @@ extern List StackFreeList; /* The main process (the one that executes main()) */ struct Process MainProcess; -static void proc_init_struct(Process* proc) + +#if CONFIG_KERN_MONITOR +List MonitorProcs; + +static void monitor_init(void) +{ + INITLIST(&MonitorProcs); +} + +static void monitor_add(Process* proc, cpustack_t* stack_base, size_t stack_size) +{ + proc->monitor.stack_base = stack_base; + proc->monitor.stack_size = stack_size; + + ADDTAIL(&MonitorProcs, &proc->monitor.link); +} + +static void monitor_remove(Process* proc) +{ + REMOVE(&proc->monitor.link); +} + +#define MONITOR_NODE_TO_PROCESS(node) \ + (struct Process*)((char*)(node) - offsetof(struct Process, monitor.link)) + +size_t monitor_check_stack(cpustack_t* stack_base, size_t stack_size) +{ + cpustack_t* beg; + cpustack_t* cur; + cpustack_t* end; + size_t sp_free; + + beg = stack_base; + end = stack_base + stack_size / sizeof(cpustack_t) - 1; + + if (CPU_STACK_GROWS_UPWARD) + { + cur = beg; + beg = end; + end = cur; + } + + cur = beg; + while (cur != end) + { + if (*cur != CONFIG_KERN_STACKFILLCODE) + break; + + if (CPU_STACK_GROWS_UPWARD) + cur--; + else + cur++; + } + + sp_free = ABS(cur - beg) * sizeof(cpustack_t); + return sp_free; +} + +void monitor_debug_stacks(void) { - /* Avoid warning for unused argument */ - (void)proc; + struct Process* p; + + for (p = MONITOR_NODE_TO_PROCESS(MonitorProcs.head); + p->monitor.link.succ; + p = MONITOR_NODE_TO_PROCESS(p->monitor.link.succ)) + { + size_t free = monitor_check_stack(p->monitor.stack_base, p->monitor.stack_size); + kprintf("TCB: %x sp_base: %x sp_size: %x sp_free: %x\n", (uint16_t)p, (uint16_t)p->monitor.stack_base, (uint16_t)p->monitor.stack_size, (uint16_t)free); + } +} + +#endif + +static void proc_init_struct(Process* proc) +{ #if CONFIG_KERN_SIGNALS proc->sig_recv = 0; #endif @@ -91,11 +170,19 @@ void proc_init(void) { INITLIST(&ProcReadyList); +#if CONFIG_KERN_MONITOR + monitor_init(); +#endif + /* We "promote" the current context into a real process. The only thing we have - to do is create a PCB and make it current. We don't need to setup the stack - pointer because it will be written the first time we switch to another process. */ + * to do is create a PCB and make it current. We don't need to setup the stack + * pointer because it will be written the first time we switch to another process. + */ proc_init_struct(&MainProcess); CurrentProcess = &MainProcess; + + /* Make sure the assembly routine is up-to-date with us */ + ASSERT(asm_switch_version() == 1); } @@ -108,6 +195,7 @@ void proc_init(void) Process *proc_new(void (*entry)(void), size_t stacksize, cpustack_t *stack_base) { Process *proc; + cpuflags_t flags; size_t i; size_t proc_size_words = ROUND2(sizeof(Process), sizeof(cpustack_t)) / sizeof(cpustack_t); #if CONFIG_KERN_HEAP @@ -141,10 +229,10 @@ Process *proc_new(void (*entry)(void), size_t stacksize, cpustack_t *stack_base) ASSERT(stacksize); #endif -#ifdef _DEBUG +#if CONFIG_KERN_MONITOR /* Fill-in the stack with a special marker to help debugging */ memset(stack_base, CONFIG_KERN_STACKFILLCODE, stacksize / sizeof(cpustack_t)); -#endif /* _DEBUG */ +#endif /* Initialize the process control block */ if (CPU_STACK_GROWS_UPWARD) @@ -180,9 +268,13 @@ Process *proc_new(void (*entry)(void), size_t stacksize, cpustack_t *stack_base) CPU_PUSH_WORD(proc->stack, CPU_REG_INIT_VALUE(i)); /* Add to ready list */ - DISABLE_INTS; + DISABLE_IRQSAVE(flags); SCHED_ENQUEUE(proc); - ENABLE_INTS; + ENABLE_IRQRESTORE(flags); + +#if CONFIG_KERN_MONITOR + monitor_add(proc, stack_base, stacksize); +#endif return proc; } @@ -203,29 +295,30 @@ void proc_schedule(void) * being switched out. */ static Process *old_process; + static cpuflags_t flags; /* Remember old process to save its context later */ old_process = CurrentProcess; - CurrentProcess = NULL; - /* Poll on the ready queue for the first ready process - */ - for(;;) /* forever */ + /* Poll on the ready queue for the first ready process */ + DISABLE_IRQSAVE(flags); + while (!(CurrentProcess = (struct Process*)REMHEAD(&ProcReadyList))) { - /* Do CPU specific idle processing (ARGH, should be moved to the end of the loop!) */ + /* + * Make sure we physically reenable interrupts here, no matter what + * the current task status is. This is important because if we + * are idle-spinning, we must allow interrupts, otherwise no + * process will ever wake up. + * + * \todo If there was a way to code sig_wait so that it does not + * disable interrupts while waiting, there would not be any + * reason to do this. + */ + ENABLE_INTS; SCHEDULER_IDLE; - DISABLE_INTS; - if (!ISLISTEMPTY(&ProcReadyList)) - { - /* Get process from ready list */ - CurrentProcess = (Process *)ProcReadyList.head; - REMOVE((Node *)CurrentProcess); - ENABLE_INTS; - break; - } - ENABLE_INTS; } + ENABLE_IRQRESTORE(flags); /* Optimization: don't switch contexts when the active * process has not changed. @@ -282,6 +375,10 @@ void proc_exit(void) */ #endif /* ARCH_EMUL */ +#if CONFIG_KERN_MONITOR + monitor_remove(CurrentProcess); +#endif + CurrentProcess = NULL; proc_schedule(); /* not reached */ @@ -293,9 +390,13 @@ void proc_exit(void) */ void proc_switch(void) { - DISABLE_INTS; + /* Just like proc_schedule, this function must not have auto variables. */ + static cpuflags_t flags; + + DISABLE_IRQSAVE(flags); SCHED_ENQUEUE(CurrentProcess); - ENABLE_INTS; + ENABLE_IRQRESTORE(flags); + proc_schedule(); } diff --git a/kern/proc_p.h b/kern/proc_p.h index 812131bd..5daf0dd0 100755 --- a/kern/proc_p.h +++ b/kern/proc_p.h @@ -15,6 +15,10 @@ /* * $Log$ + * Revision 1.4 2004/07/30 14:24:16 rasky + * Task switching con salvataggio perfetto stato di interrupt (SR) + * Kernel monitor per dump informazioni su stack dei processi + * * Revision 1.3 2004/07/14 14:18:09 rasky * Merge da SC: Rimosso timer dentro il task, che è uno spreco di memoria per troppi task * @@ -46,7 +50,6 @@ #include "config_kern.h" #include - typedef struct Process { Node link; /*!< Link Process into scheduler lists */ @@ -62,6 +65,16 @@ typedef struct Process cpustack_t *stack_base; /*!< Base of process stack */ size_t stack_size; /*!< Size of process stack */ #endif + +#if CONFIG_KERN_MONITOR + struct ProcMonitor + { + Node link; + cpustack_t* stack_base; + size_t stack_size; + } monitor; +#endif + } Process; diff --git a/kern/signal.c b/kern/signal.c index 5c62e50a..fe4ba314 100755 --- a/kern/signal.c +++ b/kern/signal.c @@ -19,6 +19,10 @@ /* * $Log$ + * Revision 1.3 2004/07/30 14:24:16 rasky + * Task switching con salvataggio perfetto stato di interrupt (SR) + * Kernel monitor per dump informazioni su stack dei processi + * * Revision 1.2 2004/06/03 11:27:09 bernie * Add dual-license information. * @@ -42,11 +46,12 @@ sigset_t sig_check(sigset_t sigs) { sigset_t result; + cpuflags_t flags; - DISABLE_INTS; + DISABLE_IRQSAVE(flags); result = CurrentProcess->sig_recv & sigs; CurrentProcess->sig_recv &= ~sigs; - ENABLE_INTS; + ENABLE_IRQRESTORE(flags); return result; } @@ -58,24 +63,22 @@ sigset_t sig_check(sigset_t sigs) sigset_t sig_wait(sigset_t sigs) { sigset_t result; + cpuflags_t flags; - DISABLE_INTS; + DISABLE_IRQSAVE(flags); - for(;;) + /* Loop until we get at least one of the signals */ + while (!(result = CurrentProcess->sig_recv & sigs)) { - /* Check if we got at least one of the signals */ - if ((result = CurrentProcess->sig_recv & sigs)) - { - /* Yes, clear signals and return */ - CurrentProcess->sig_recv &= ~sigs; - ENABLE_INTS; - return result; - } - - /* No, go to sleep and proc_schedule() another process */ + /* go to sleep and proc_schedule() another process */ CurrentProcess->sig_wait = sigs; proc_schedule(); } + + /* Signals found: clear them and return */ + CurrentProcess->sig_recv &= ~sigs; + ENABLE_IRQRESTORE(flags); + return result; } diff --git a/kern/switch_dsp56k.c b/kern/switch_dsp56k.c index 51a380f4..d99a6487 100755 --- a/kern/switch_dsp56k.c +++ b/kern/switch_dsp56k.c @@ -14,6 +14,10 @@ /* * $Log$ + * Revision 1.3 2004/07/30 14:24:16 rasky + * Task switching con salvataggio perfetto stato di interrupt (SR) + * Kernel monitor per dump informazioni su stack dei processi + * * Revision 1.2 2004/06/03 11:27:09 bernie * Add dual-license information. * @@ -26,31 +30,6 @@ void asm_switch_context(void ** new_sp/*R2*/, void ** save_sp/*R3*/); asm void asm_switch_context(void ** new_sp, void ** save_sp) { lea (SP)+ - move n,x:(SP)+ - move x0,x:(SP)+ - move y0,x:(SP)+ - move y1,x:(SP)+ - move a0,x:(SP)+ - move a1,x:(SP)+ - move a2,x:(SP)+ - move b0,x:(SP)+ - move b1,x:(SP)+ - move b2,x:(SP)+ - move r0,x:(SP)+ - move r1,x:(SP)+ - move r2,x:(SP)+ - move r3,x:(SP)+ - - move omr,x:(SP)+ - move la,x:(SP)+ - move m01,x:(SP)+ - move lc,x:(SP)+ - - ; - ; save hardware stack - ; - move hws,x:(SP)+ - move hws,x:(SP)+ ; From the manual: ; The compiler uses page 0 address locations X: 0x0030 - 0x003F as register @@ -80,13 +59,16 @@ asm void asm_switch_context(void ** new_sp, void ** save_sp) move y1,x:(SP) ; - ; 28 words have been pushed on the stack. + ; Switch stacks nop move SP, x:(R3) nop move x:(R2), SP nop + ; + ; restore all saved registers + ; pop y1 move y1,x:<$3F pop y1 @@ -104,38 +86,13 @@ asm void asm_switch_context(void ** new_sp, void ** save_sp) pop y1 move y1,x:<$38 - ; - ; restore hardware stack - ; - move hws,la ; Clear HWS to ensure proper reload - move hws,la - pop HWS - pop HWS - - ; - ; restore all saved registers - ; - pop lc - pop m01 - pop la - pop omr - - pop r3 - pop r2 - pop r1 - pop r0 - pop b2 - pop b1 - pop b0 - pop a2 - pop a1 - pop a0 - - pop y1 - pop y0 - pop x0 + ; SR is already pushed on the stack (normal call context). Use RTI to restore + ; it, so that interrupt status is preserved across the tasks. + rti +} - pop n - - rts +int asm_switch_version(void); +int asm_switch_version(void) +{ + return 1; }