]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - examples/sched.c
Fix SDRAM timing on Purple board
[karo-tx-uboot.git] / examples / sched.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License as
4  * published by the Free Software Foundation; either version 2 of
5  * the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
15  * MA 02111-1307 USA
16  */
17
18 #include <common.h>
19 #include <syscall.h>
20
21 /*
22  * Author: Arun Dharankar <ADharankar@ATTBI.Com>
23  *
24  * A very simple thread/schedular model:
25  *   - only one master thread, and no parent child relation maintained
26  *   - parent thread cannot be stopped or deleted
27  *   - no permissions or credentials
28  *   - no elaborate safety checks
29  *   - cooperative multi threading
30  *   - Simple round-robin scheduleing with no priorities
31  *   - no metering/statistics collection
32  *
33  * Basic idea of implementing this is to allow more than one tests to
34  * execute "simultaneously".
35  *
36  * This may be modified such thread_yield may be called in syscalls, and
37  * timer interrupts.
38  */
39
40
41 #define MAX_THREADS 8
42
43 #define CTX_SIZE 512
44 #define STK_SIZE 8*1024
45
46 #define STATE_EMPTY 0
47 #define STATE_RUNNABLE 1
48 #define STATE_STOPPED 2
49 #define STATE_TERMINATED 2
50
51 #define MASTER_THREAD 0
52
53 #define RC_FAILURE      (-1)
54 #define RC_SUCCESS      (0)
55
56 typedef vu_char *jmp_ctx;
57 unsigned long setctxsp (vu_char *sp);
58 int ppc_setjmp(jmp_ctx env);
59 void ppc_longjmp(jmp_ctx env, int val);
60 #define setjmp  ppc_setjmp
61 #define longjmp ppc_longjmp
62
63 struct lthread {
64         int state;
65         int retval;
66         char stack[STK_SIZE];
67         uchar context[CTX_SIZE];
68         int (*func) (void *);
69         void *arg;
70 };
71 static volatile struct lthread lthreads[MAX_THREADS];
72 static volatile int current_tid = MASTER_THREAD;
73
74
75 static uchar dbg = 0;
76
77 #define PDEBUG(fmt, args...)     {                                      \
78         if(dbg != 0) {                                                  \
79                 mon_printf("[%s %d %s]: ",__FILE__,__LINE__,__FUNCTION__);\
80                 mon_printf(fmt, ##args);                                \
81                 mon_printf("\n");                                       \
82         }                                                               \
83 }
84
85 static int testthread (void *);
86 static void sched_init (void);
87 static int thread_create (int (*func) (void *), void *arg);
88 static int thread_start (int id);
89 static void thread_yield (void);
90 static int thread_delete (int id);
91 static int thread_join (int *ret);
92
93 #if 0                                                   /* not used yet */
94 static int thread_stop (int id);
95 #endif                                                  /* not used yet */
96
97 /* An example of schedular test */
98
99 #define NUMTHREADS 7
100 int sched (bd_t * bd, int ac, char *av[])
101 {
102         int i, j;
103         int tid[NUMTHREADS];
104         int names[NUMTHREADS];
105
106         sched_init ();
107
108         for (i = 0; i < NUMTHREADS; i++) {
109                 names[i] = i;
110                 j = thread_create (testthread, (void *) &names[i]);
111                 if (j == RC_FAILURE)
112                         mon_printf ("schedtest: Failed to create thread %d\n", i);
113                 if (j > 0) {
114                         mon_printf ("schedtest: Created thread with id %d, name %d\n",
115                                                 j, i);
116                         tid[i] = j;
117                 }
118         }
119         mon_printf ("schedtest: Threads created\n");
120
121         mon_printf ("sched_test: function=0x%08x\n", testthread);
122         for (i = 0; i < NUMTHREADS; i++) {
123                 mon_printf ("schedtest: Setting thread %d runnable\n", tid[i]);
124                 thread_start (tid[i]);
125                 thread_yield ();
126         }
127         mon_printf ("schedtest: Started %d threads\n", NUMTHREADS);
128
129         while (1) {
130                 mon_printf ("schedtest: Waiting for threads to complete\n");
131                 if (mon_tstc () && mon_getc () == 0x3) {
132                         mon_printf ("schedtest: Aborting threads...\n");
133                         for (i = 0; i < NUMTHREADS; i++) {
134                                 mon_printf ("schedtest: Deleting thread %d\n", tid[i]);
135                                 thread_delete (tid[i]);
136                         }
137                         return RC_SUCCESS;
138                 }
139                 j = -1;
140                 i = thread_join (&j);
141                 if (i == RC_FAILURE) {
142                         mon_printf ("schedtest: No threads pending, "
143                                                 "exiting schedular test\n");
144                         return RC_SUCCESS;
145                 }
146                 mon_printf ("schedtest: thread is %d returned %d\n", i, j);
147                 thread_yield ();
148         }
149
150         return RC_SUCCESS;
151 }
152
153 static int testthread (void *name)
154 {
155         int i;
156
157         mon_printf ("testthread: Begin executing thread, myname %d, &i=0x%08x\n",
158                          *(int *) name, &i);
159
160         mon_printf ("Thread %02d, i=%d\n", *(int *) name);
161
162         for (i = 0; i < 0xffff * (*(int *) name + 1); i++) {
163                 if (mon_tstc () && mon_getc () == 0x3) {
164                         mon_printf ("testthread: myname %d terminating.\n",
165                                                 *(int *) name);
166                         return *(int *) name + 1;
167                 }
168
169                 if (i % 100 == 0)
170                         thread_yield ();
171         }
172
173         mon_printf ("testthread: returning %d, i=0x%x\n",
174                                 *(int *) name + 1, i);
175
176         return *(int *) name + 1;
177 }
178
179
180 static void sched_init (void)
181 {
182         int i;
183
184         for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++)
185                 lthreads[i].state = STATE_EMPTY;
186
187         current_tid = MASTER_THREAD;
188         lthreads[current_tid].state = STATE_RUNNABLE;
189         PDEBUG ("sched_init: master context = 0x%08x",
190                    lthreads[current_tid].context);
191         return;
192 }
193
194 static void thread_yield (void)
195 {
196         static int i;
197
198         PDEBUG ("thread_yield: current tid=%d", current_tid);
199
200 #define SWITCH(new)                                                     \
201         if(lthreads[new].state == STATE_RUNNABLE) {                     \
202                 PDEBUG("thread_yield: %d match, ctx=0x%08x",            \
203                         new, lthreads[current_tid].context);            \
204                 if(setjmp(lthreads[current_tid].context) == 0) {        \
205                         current_tid = new;                              \
206                         PDEBUG("thread_yield: tid %d returns 0",        \
207                                 new);                                   \
208                         longjmp(lthreads[new].context, 1);              \
209                 } else {                                                \
210                         PDEBUG("thread_yield: tid %d returns 1",        \
211                                 new);                                   \
212                         return;                                         \
213                 }                                                       \
214         }
215
216         for (i = current_tid + 1; i < MAX_THREADS; i++) {
217                 SWITCH (i);
218         }
219
220         if (current_tid != 0) {
221                 for (i = 0; i <= current_tid; i++) {
222                         SWITCH (i);
223                 }
224         }
225
226         PDEBUG ("thread_yield: returning from thread_yield");
227         return;
228 }
229
230 static int thread_create (int (*func) (void *), void *arg)
231 {
232         int i;
233
234         for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) {
235                 if (lthreads[i].state == STATE_EMPTY) {
236                         lthreads[i].state = STATE_STOPPED;
237                         lthreads[i].func = func;
238                         lthreads[i].arg = arg;
239                         PDEBUG ("thread_create: returns new tid %d", i);
240                         return i;
241                 }
242         }
243
244         PDEBUG ("thread_create: returns failure");
245         return RC_FAILURE;
246 }
247
248 static int thread_delete (int id)
249 {
250         if (id <= MASTER_THREAD || id > MAX_THREADS)
251                 return RC_FAILURE;
252
253         if (current_tid == id)
254                 return RC_FAILURE;
255
256         lthreads[id].state = STATE_EMPTY;
257         return RC_SUCCESS;
258 }
259
260 static void thread_launcher (void)
261 {
262         PDEBUG ("thread_launcher: invoking func=0x%08x",
263                    lthreads[current_tid].func);
264
265         lthreads[current_tid].retval =
266                         lthreads[current_tid].func (lthreads[current_tid].arg);
267
268         PDEBUG ("thread_launcher: tid %d terminated", current_tid);
269
270         lthreads[current_tid].state = STATE_TERMINATED;
271         thread_yield ();
272         mon_printf ("thread_launcher: should NEVER get here!\n");
273
274         return;
275 }
276
277 static int thread_start (int id)
278 {
279         PDEBUG ("thread_start: id=%d", id);
280         if (id <= MASTER_THREAD || id > MAX_THREADS) {
281                 return RC_FAILURE;
282         }
283
284         if (lthreads[id].state != STATE_STOPPED)
285                 return RC_FAILURE;
286
287         if (setjmp (lthreads[current_tid].context) == 0) {
288                 lthreads[id].state = STATE_RUNNABLE;
289                 current_tid = id;
290                 PDEBUG ("thread_start: to be stack=0%08x", lthreads[id].stack);
291                 setctxsp (&lthreads[id].stack[STK_SIZE]);
292                 thread_launcher ();
293         }
294
295         PDEBUG ("thread_start: Thread id=%d started, parent returns", id);
296
297         return RC_SUCCESS;
298 }
299
300 #if 0                                                   /* not used so far */
301 static int thread_stop (int id)
302 {
303         if (id <= MASTER_THREAD || id >= MAX_THREADS)
304                 return RC_FAILURE;
305
306         if (current_tid == id)
307                 return RC_FAILURE;
308
309         lthreads[id].state = STATE_STOPPED;
310         return RC_SUCCESS;
311 }
312 #endif                                                  /* not used so far */
313
314 static int thread_join (int *ret)
315 {
316         int i, j = 0;
317
318         PDEBUG ("thread_join: *ret = %d", *ret);
319
320         if (!(*ret == -1 || *ret > MASTER_THREAD || *ret < MAX_THREADS)) {
321                 PDEBUG ("thread_join: invalid tid %d", *ret);
322                 return RC_FAILURE;
323         }
324
325         if (*ret == -1) {
326                 PDEBUG ("Checking for tid = -1");
327                 while (1) {
328                         /* PDEBUG("thread_join: start while-loopn"); */
329                         j = 0;
330                         for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) {
331                                 if (lthreads[i].state == STATE_TERMINATED) {
332                                         *ret = lthreads[i].retval;
333                                         lthreads[i].state = STATE_EMPTY;
334                                         /* PDEBUG("thread_join: returning retval %d of tid %d",
335                                            ret, i); */
336                                         return RC_SUCCESS;
337                                 }
338
339                                 if (lthreads[i].state != STATE_EMPTY) {
340                                         PDEBUG ("thread_join: %d used slots tid %d state=%d",
341                                                    j, i, lthreads[i].state);
342                                         j++;
343                                 }
344                         }
345                         if (j == 0) {
346                                 PDEBUG ("thread_join: all slots empty!");
347                                 return RC_FAILURE;
348                         }
349                         /*  PDEBUG("thread_join: yielding"); */
350                         thread_yield ();
351                         /*  PDEBUG("thread_join: back from yield"); */
352                 }
353         }
354
355         if (lthreads[*ret].state == STATE_TERMINATED) {
356                 i = *ret;
357                 *ret = lthreads[*ret].retval;
358                 lthreads[*ret].state = STATE_EMPTY;
359                 PDEBUG ("thread_join: returing %d for tid %d", *ret, i);
360                 return RC_SUCCESS;
361         }
362
363         PDEBUG ("thread_join: thread %d is not terminated!", *ret);
364         return RC_FAILURE;
365 }