Merge da SC: Rimosso timer dentro il task, che è uno spreco di memoria per troppi...
[bertos.git] / kern / proc.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2001,2004 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
6  * This file is part of DevLib - See devlib/README for information.
7  * -->
8  *
9  * \brief Simple realtime multitasking scheduler.
10  *        Context switching is only done cooperatively.
11  *
12  * \version $Id$
13  *
14  * \author Bernardo Innocenti <bernie@develer.com>
15  * \author Stefano Fedrigo <aleph@develer.com>
16  */
17
18 /*
19  * $Log$
20  * Revision 1.5  2004/07/14 14:18:09  rasky
21  * Merge da SC: Rimosso timer dentro il task, che è uno spreco di memoria per troppi task
22  *
23  * Revision 1.4  2004/07/13 19:21:28  aleph
24  * Avoid warning for unused arg when compiled without some CONFIG_KERN_xx options
25  *
26  * Revision 1.3  2004/06/06 18:37:57  bernie
27  * Rename event macros to look like regular functions.
28  *
29  * Revision 1.2  2004/06/03 11:27:09  bernie
30  * Add dual-license information.
31  *
32  * Revision 1.1  2004/05/23 17:27:00  bernie
33  * Import kern/ subdirectory.
34  *
35  */
36
37 #include "cpu.h"
38 #include "proc_p.h"
39 #include "proc.h"
40 #include "event.h"
41 #include "hw.h"
42 #include <drv/kdebug.h>
43
44 #include <string.h> /* memset() */
45
46 /* CPU dependent context switching routines */
47 extern void asm_switch_context(cpustack_t **new_sp, cpustack_t **save_sp);
48
49 /*
50  * The scheduer tracks ready and waiting processes
51  * by enqueuing them in these lists. A pointer to the currently
52  * running process is stored in the CurrentProcess pointer.
53  *
54  * NOTE: these variables are protected by DI/EI locking
55  */
56 REGISTER Process *CurrentProcess;
57 REGISTER List     ProcReadyList;
58
59 #if CONFIG_KERN_PREEMPTIVE
60 /*
61  * The time sharing scheduler forces a task switch when
62  * the current process has consumed its quantum.
63  */
64 uint16_t Quantum;
65 #endif
66
67
68 /* In Win32 we must emulate stack on the real process stack */
69 #if (ARCH & ARCH_EMUL)
70 extern List StackFreeList;
71 #endif
72
73 /* The main process (the one that executes main()) */
74 struct Process MainProcess;
75
76 static void proc_init_struct(Process* proc)
77 {
78         /* Avoid warning for unused argument */
79         (void)proc;
80
81 #if CONFIG_KERN_SIGNALS
82         proc->sig_recv = 0;
83 #endif
84
85 #if CONFIG_KERN_HEAP
86         proc->flags = 0;
87 #endif
88 }
89
90 void proc_init(void)
91 {
92         INITLIST(&ProcReadyList);
93
94         /* We "promote" the current context into a real process. The only thing we have
95            to do is create a PCB and make it current. We don't need to setup the stack
96            pointer because it will be written the first time we switch to another process. */
97         proc_init_struct(&MainProcess);
98         CurrentProcess = &MainProcess;
99 }
100
101
102 /*!
103  * Create a new process, starting at the provided entry point.
104  *
105  * \return Process structure of new created process
106  *         if successful, NULL otherwise.
107  */
108 Process *proc_new(void (*entry)(void), size_t stacksize, cpustack_t *stack_base)
109 {
110         Process *proc;
111         size_t i;
112         size_t proc_size_words = ROUND2(sizeof(Process), sizeof(cpustack_t)) / sizeof(cpustack_t);
113 #if CONFIG_KERN_HEAP
114         bool free_stack = false;
115 #endif
116
117 #if (ARCH & ARCH_EMUL)
118         /* Ignore stack provided by caller
119         * and use the large enough default instead
120         */
121         stack_base = (cpustack_t *)StackFreeList.head;
122         REMOVE((Node *)stack_base);
123         stacksize = DEF_STACKSIZE;
124 #elif CONFIG_KERN_HEAP
125         /* Did the caller provide a stack for us? */
126         if (!stack_base)
127         {
128                 /* Did the caller specify the desired stack size? */
129                 if (!stacksize)
130                         stacksize = CONFIG_KERN_DEFSTACKSIZE + sizeof(Process);
131
132                 /* Allocate stack dinamically */
133                 if (!(stack_base = heap_alloc(stacksize)))
134                         return NULL;
135
136                 free_stack = true;
137         }
138 #else
139         /* Stack must have been provided by the user */
140         ASSERT(stack_base);
141         ASSERT(stacksize);
142 #endif
143
144 #ifdef _DEBUG
145         /* Fill-in the stack with a special marker to help debugging */
146         memset(stack_base, CONFIG_KERN_STACKFILLCODE, stacksize / sizeof(cpustack_t));
147 #endif /* _DEBUG */
148
149         /* Initialize the process control block */
150         if (CPU_STACK_GROWS_UPWARD)
151         {
152                 proc = (Process*)stack_base;
153                 proc->stack = stack_base + proc_size_words;
154                 if (CPU_SP_ON_EMPTY_SLOT)
155                         proc->stack++;
156         }
157         else
158         {
159                 proc = (Process*)(stack_base + stacksize / sizeof(cpustack_t) - proc_size_words);
160                 proc->stack = (cpustack_t*)proc;
161                 if (CPU_SP_ON_EMPTY_SLOT)
162                         proc->stack--;
163         }
164
165         proc_init_struct(proc);
166
167 #if CONFIG_KERN_HEAP
168         proc->stack_base = stack_base;
169         proc->stack_size = stack_size;
170         if (free_stack)
171                 proc->flags |= PF_FREESTACK;
172 #endif
173
174         /* Initialize process stack frame */
175         CPU_PUSH_CALL_CONTEXT(proc->stack, proc_exit);
176         CPU_PUSH_CALL_CONTEXT(proc->stack, entry);
177
178         /* Push a clean set of CPU registers for asm_switch_context() */
179         for (i = 0; i < CPU_SAVED_REGS_CNT; i++)
180                 CPU_PUSH_WORD(proc->stack, CPU_REG_INIT_VALUE(i));
181
182         /* Add to ready list */
183         DISABLE_INTS;
184         SCHED_ENQUEUE(proc);
185         ENABLE_INTS;
186
187         return proc;
188 }
189
190
191 /*!
192  * System scheduler: pass CPU control to the next process in
193  * the ready queue.
194  *
195  * Saving and restoring the context on the stack is done
196  * by a CPU-dependent support routine which must usually be
197  * written in assembly.
198  */
199 void proc_schedule(void)
200 {
201         /* This function must not have any "auto" variables, otherwise
202          * the compiler might put them on the stack of the process
203          * being switched out.
204          */
205         static Process *old_process;
206
207         /* Remember old process to save its context later */
208         old_process = CurrentProcess;
209         CurrentProcess = NULL;
210
211         /* Poll on the ready queue for the first ready process
212          */
213         for(;;) /* forever */
214         {
215                 /* Do CPU specific idle processing (ARGH, should be moved to the end of the loop!) */
216                 SCHEDULER_IDLE;
217
218                 DISABLE_INTS;
219                 if (!ISLISTEMPTY(&ProcReadyList))
220                 {
221                         /* Get process from ready list */
222                         CurrentProcess = (Process *)ProcReadyList.head;
223                         REMOVE((Node *)CurrentProcess);
224                         ENABLE_INTS;
225                         break;
226                 }
227                 ENABLE_INTS;
228         }
229
230         /* Optimization: don't switch contexts when the active
231          * process has not changed.
232          */
233         if (CurrentProcess != old_process)
234         {
235                 static cpustack_t* dummy;
236
237 #if CONFIG_KERN_PREEMPTIVE
238                 /* Reset quantum for this process */
239                 Quantum = CONFIG_KERN_QUANTUM;
240 #endif
241
242                 /* Save context of old process and switch to new process. If there is no
243                  * old process, we save the old stack pointer into a dummy variable that
244                  * we ignore. In fact, this happens only when the old process has just
245                  * exited.
246                  * TODO: Instead of physically clearing the process at exit time, a zombie
247                  * list should be created.
248                  */
249                 asm_switch_context(&CurrentProcess->stack, old_process ? &old_process->stack : &dummy);
250         }
251
252         /* This RET resumes the execution on the new process */
253 }
254
255
256 /*!
257  * Terminate the current process
258  */
259 void proc_exit(void)
260 {
261 #if CONFIG_KERN_HEAP
262         /* The following code is BROKEN.
263          * We are freeing our own stack before entering proc_schedule()
264          * BAJO: A correct fix would be to rearrange the scheduler with
265          *  an additional parameter which frees the old stack/process
266          *  after a context switch.
267          */
268         if (CurrentProcess->flags & PF_FREESTACK)
269                 heap_free(CurrentProcess->stack_base, CurrentProcess->stack_size);
270         heap_free(CurrentProcess);
271 #endif
272
273 #if (ARCH & ARCH_EMUL)
274 #error This is wrong
275         /* Reinsert process stack in free list */
276         ADDHEAD(&StackFreeList, (Node *)(CurrentProcess->stack
277                 - (DEF_STACKSIZE / sizeof(cpustack_t))));
278
279         /* NOTE: At this point the first two words of what used
280          * to be our stack contain a list node. From now on, we
281          * rely on the compiler not reading/writing the stack.
282          */
283 #endif /* ARCH_EMUL */
284
285         CurrentProcess = NULL;
286         proc_schedule();
287         /* not reached */
288 }
289
290
291 /*!
292  * Co-operative context switch
293  */
294 void proc_switch(void)
295 {
296         DISABLE_INTS;
297         SCHED_ENQUEUE(CurrentProcess);
298         ENABLE_INTS;
299         proc_schedule();
300 }
301
302
303 /*!
304  * Get the pointer to the current process
305  */
306 struct Process *proc_current(void)
307 {
308         return CurrentProcess;
309 }
310
311
312 #if 0 /* Simple testcase for the scheduler */
313
314 /*!
315  * Proc scheduling test subthread 1
316  */
317 static void NORETURN proc_test_thread1(void)
318 {
319         for (;;)
320         {
321                 kputs(">task 1\n");
322                 timer_delay(50);
323                 proc_switch();
324         }
325 }
326
327 /*!
328  * Proc scheduling test subthread 2
329  */
330 static void NORETURN proc_test_thread2(void)
331 {
332         for (;;)
333         {
334                 kputs(">task 2\n");
335                 timer_delay(75);
336                 proc_switch();
337         }
338 }
339
340 static cpustack_t proc_test_stack1[CONFIG_KERN_DEFSTACKSIZE/sizeof(cpustack_t)];
341 static cpustack_t proc_test_stack2[CONFIG_KERN_DEFSTACKSIZE/sizeof(cpustack_t)];
342
343 /*!
344  * Proc scheduling test
345  */
346 void NORETURN proc_test(void)
347 {
348         proc_new(proc_test_thread1, sizeof(proc_test_stack1), proc_test_stack1);
349         proc_new(proc_test_thread2, sizeof(proc_test_stack2), proc_test_stack2);
350         kputs("Created tasks\n");
351
352         kputs("stack1:\n");
353         kdump(proc_test_stack1+sizeof(proc_test_stack1)-64, 64);
354         kputs("stack2:\n");
355         kdump(proc_test_stack2+sizeof(proc_test_stack1)-64, 64);
356
357         for (;;)
358         {
359                 kputs(">main task\n");
360                 timer_delay(93);
361                 proc_switch();
362         }
363
364         ASSERT(false);
365 }
366 #endif