From 85ec0bcc1bc40a67368461fee0435d79305168b1 Mon Sep 17 00:00:00 2001 From: wdenk Date: Mon, 31 Mar 2003 16:34:49 +0000 Subject: * Patch by Arun Dharankar, 24 Mar 2003: - add threads / scheduler example code * Add patches by Robert Schwebel, 31 Mar 2003: - add ctrl-c support for kermit download - align bdinfo output on ARM * Add CPU ID, version, and clock speed for INCA-IP --- examples/Makefile | 11 +- examples/ppc_longjmp.S | 79 +++++++++++ examples/ppc_setjmp.S | 83 ++++++++++++ examples/sched.c | 361 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 533 insertions(+), 1 deletion(-) create mode 100644 examples/ppc_longjmp.S create mode 100644 examples/ppc_setjmp.S create mode 100644 examples/sched.c (limited to 'examples') diff --git a/examples/Makefile b/examples/Makefile index b9056dd161..163b404e40 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -28,6 +28,11 @@ include $(TOPDIR)/config.mk SREC = hello_world.srec BIN = hello_world.bin +ifeq ($(ARCH),ppc) +SREC += sched.srec +BIN += sched.bin +endif + ifeq ($(CPU),mips) SREC = BIN = @@ -36,6 +41,7 @@ endif # The following example is pretty 8xx specific... ifeq ($(CPU),mpc8xx) SREC += timer.srec +BIN += timer.bin endif # Utility for resetting i82559 EEPROM @@ -45,8 +51,11 @@ endif OBJS = $(SREC:.srec=.o) -LIB = libsyscall.a +LIB = libsyscall.a LIBAOBJS= syscall.o +ifeq ($(ARCH),ppc) +LIBAOBJS+= $(ARCH)_longjmp.o $(ARCH)_setjmp.o +endif LIBCOBJS= LIBOBJS = $(LIBAOBJS) $(LIBCOBJS) diff --git a/examples/ppc_longjmp.S b/examples/ppc_longjmp.S new file mode 100644 index 0000000000..ad8285a86b --- /dev/null +++ b/examples/ppc_longjmp.S @@ -0,0 +1,79 @@ +/* longjmp for PowerPC. + Copyright (C) 1995, 1996, 1997, 1999, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include + +# define JB_GPR1 0 /* Also known as the stack pointer */ +# define JB_GPR2 1 +# define JB_LR 2 /* The address we will return to */ +# define JB_GPRS 3 /* GPRs 14 through 31 are saved, 18 in total */ +# define JB_CR 21 /* Condition code registers. */ +# define JB_FPRS 22 /* FPRs 14 through 31 are saved, 18*2 words total */ +# define JB_SIZE (58*4) + +#define FP(x...) x +#define FP(x...) x + +.globl longjmp; + +longjmp: + lwz r1,(JB_GPR1*4)(r3) + lwz r2,(JB_GPR2*4)(r3) + lwz r0,(JB_LR*4)(r3) + lwz r14,((JB_GPRS+0)*4)(r3) +FP( lfd 14,((JB_FPRS+0*2)*4)(r3)) + lwz r15,((JB_GPRS+1)*4)(r3) +FP( lfd 15,((JB_FPRS+1*2)*4)(r3)) + lwz r16,((JB_GPRS+2)*4)(r3) +FP( lfd 16,((JB_FPRS+2*2)*4)(r3)) + lwz r17,((JB_GPRS+3)*4)(r3) +FP( lfd 17,((JB_FPRS+3*2)*4)(r3)) + lwz r18,((JB_GPRS+4)*4)(r3) +FP( lfd 18,((JB_FPRS+4*2)*4)(r3)) + lwz r19,((JB_GPRS+5)*4)(r3) +FP( lfd 19,((JB_FPRS+5*2)*4)(r3)) + lwz r20,((JB_GPRS+6)*4)(r3) +FP( lfd 20,((JB_FPRS+6*2)*4)(r3)) + mtlr r0 + lwz r21,((JB_GPRS+7)*4)(r3) +FP( lfd 21,((JB_FPRS+7*2)*4)(r3)) + lwz r22,((JB_GPRS+8)*4)(r3) +FP( lfd 22,((JB_FPRS+8*2)*4)(r3)) + lwz r0,(JB_CR*4)(r3) + lwz r23,((JB_GPRS+9)*4)(r3) +FP( lfd 23,((JB_FPRS+9*2)*4)(r3)) + lwz r24,((JB_GPRS+10)*4)(r3) +FP( lfd 24,((JB_FPRS+10*2)*4)(r3)) + lwz r25,((JB_GPRS+11)*4)(r3) +FP( lfd 25,((JB_FPRS+11*2)*4)(r3)) + mtcrf 0xFF,r0 + lwz r26,((JB_GPRS+12)*4)(r3) +FP( lfd 26,((JB_FPRS+12*2)*4)(r3)) + lwz r27,((JB_GPRS+13)*4)(r3) +FP( lfd 27,((JB_FPRS+13*2)*4)(r3)) + lwz r28,((JB_GPRS+14)*4)(r3) +FP( lfd 28,((JB_FPRS+14*2)*4)(r3)) + lwz r29,((JB_GPRS+15)*4)(r3) +FP( lfd 29,((JB_FPRS+15*2)*4)(r3)) + lwz r30,((JB_GPRS+16)*4)(r3) +FP( lfd 30,((JB_FPRS+16*2)*4)(r3)) + lwz r31,((JB_GPRS+17)*4)(r3) +FP( lfd 31,((JB_FPRS+17*2)*4)(r3)) + mr r3,r4 + blr diff --git a/examples/ppc_setjmp.S b/examples/ppc_setjmp.S new file mode 100644 index 0000000000..b718113023 --- /dev/null +++ b/examples/ppc_setjmp.S @@ -0,0 +1,83 @@ +/* setjmp for PowerPC. + Copyright (C) 1995, 1996, 1997, 1999, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include + +# define JB_GPR1 0 /* Also known as the stack pointer */ +# define JB_GPR2 1 +# define JB_LR 2 /* The address we will return to */ +# define JB_GPRS 3 /* GPRs 14 through 31 are saved, 18 in total */ +# define JB_CR 21 /* Condition code registers. */ +# define JB_FPRS 22 /* FPRs 14 through 31 are saved, 18*2 words total */ +# define JB_SIZE (58*4) + +#define FP(x...) x + +.globl setctxsp; +setctxsp: + mr r1, r3 + blr + +.globl _setjmp; +_setjmp: + stw r1,(JB_GPR1*4)(3) + mflr r0 + stw r2,(JB_GPR2*4)(3) + stw r14,((JB_GPRS+0)*4)(3) +FP( stfd 14,((JB_FPRS+0*2)*4)(3)) + stw r0,(JB_LR*4)(3) + stw r15,((JB_GPRS+1)*4)(3) +FP( stfd 15,((JB_FPRS+1*2)*4)(3)) + mfcr r0 + stw r16,((JB_GPRS+2)*4)(3) +FP( stfd 16,((JB_FPRS+2*2)*4)(3)) + stw r0,(JB_CR*4)(3) + stw r17,((JB_GPRS+3)*4)(3) +FP( stfd 17,((JB_FPRS+3*2)*4)(3)) + stw r18,((JB_GPRS+4)*4)(3) +FP( stfd 18,((JB_FPRS+4*2)*4)(3)) + stw r19,((JB_GPRS+5)*4)(3) +FP( stfd 19,((JB_FPRS+5*2)*4)(3)) + stw r20,((JB_GPRS+6)*4)(3) +FP( stfd 20,((JB_FPRS+6*2)*4)(3)) + stw r21,((JB_GPRS+7)*4)(3) +FP( stfd 21,((JB_FPRS+7*2)*4)(3)) + stw r22,((JB_GPRS+8)*4)(3) +FP( stfd 22,((JB_FPRS+8*2)*4)(3)) + stw r23,((JB_GPRS+9)*4)(3) +FP( stfd 23,((JB_FPRS+9*2)*4)(3)) + stw r24,((JB_GPRS+10)*4)(3) +FP( stfd 24,((JB_FPRS+10*2)*4)(3)) + stw r25,((JB_GPRS+11)*4)(3) +FP( stfd 25,((JB_FPRS+11*2)*4)(3)) + stw r26,((JB_GPRS+12)*4)(3) +FP( stfd 26,((JB_FPRS+12*2)*4)(3)) + stw r27,((JB_GPRS+13)*4)(3) +FP( stfd 27,((JB_FPRS+13*2)*4)(3)) + stw r28,((JB_GPRS+14)*4)(3) +FP( stfd 28,((JB_FPRS+14*2)*4)(3)) + stw r29,((JB_GPRS+15)*4)(3) +FP( stfd 29,((JB_FPRS+15*2)*4)(3)) + stw r30,((JB_GPRS+16)*4)(3) +FP( stfd 30,((JB_FPRS+16*2)*4)(3)) + stw r31,((JB_GPRS+17)*4)(3) +FP( stfd 31,((JB_FPRS+17*2)*4)(3)) + + li 3, 0 + blr diff --git a/examples/sched.c b/examples/sched.c new file mode 100644 index 0000000000..4f6f98e78f --- /dev/null +++ b/examples/sched.c @@ -0,0 +1,361 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include + +/* + * Author: Arun Dharankar + * + * A very simple thread/schedular model: + * - only one master thread, and no parent child relation maintained + * - parent thread cannot be stopped or deleted + * - no permissions or credentials + * - no elaborate safety checks + * - cooperative multi threading + * - Simple round-robin scheduleing with no priorities + * - no metering/statistics collection + * + * Basic idea of implementing this is to allow more than one tests to + * execute "simultaneously". + * + * This may be modified such thread_yield may be called in syscalls, and + * timer interrupts. + */ + + +#define MAX_THREADS 8 + +#define CTX_SIZE 512 +#define STK_SIZE 8*1024 + +#define STATE_EMPTY 0 +#define STATE_RUNNABLE 1 +#define STATE_STOPPED 2 +#define STATE_TERMINATED 2 + +#define MASTER_THREAD 0 + +#define RC_FAILURE (-1) +#define RC_SUCCESS (0) + +struct lthread { + int state; + int retval; + char stack[STK_SIZE]; + uchar context[CTX_SIZE]; + int (*func) (void *); + void *arg; +}; +static volatile struct lthread lthreads[MAX_THREADS]; +static volatile int current_tid = MASTER_THREAD; + + +static uchar dbg = 0; + +#define DEBUG(fmt, args...) { \ + if(dbg != 0) { \ + mon_printf("[%s %d %s]: ", __FILE__, __LINE__, __FUNCTION__); \ + mon_printf(fmt, ##args); \ + mon_printf("\n"); \ + } \ + } + +static int testthread (void *); +static void sched_init (void); +static int thread_create (int (*func) (void *), void *arg); +static int thread_start (int id); +static void thread_yield (void); +static int thread_delete (int id); +static int thread_join (int *ret); +#if 0 /* not used yet */ +static int thread_stop (int id); +#endif /* not used yet */ + +/* An example of schedular test */ + +#define NUMTHREADS 7 +int sched (bd_t *bd, int ac, char *av[]) +{ + int i, j; + int tid[NUMTHREADS]; + int names[NUMTHREADS]; + + sched_init (); + + for (i = 0; i < NUMTHREADS; i++) { + names[i] = i; + j = thread_create (testthread, (void *) &names[i]); + if (j == RC_FAILURE) + mon_printf ("schedtest: Failed to create thread %d\n", + i); + if (j > 0) { + mon_printf ("schedtest: Created thread with id %d, name %d\n", + j, i); + tid[i] = j; + } + } + mon_printf ("schedtest: Threads created\n"); + + mon_printf ("sched_test: function=0x%08x\n", testthread); + for (i = 0; i < NUMTHREADS; i++) { + mon_printf ("schedtest: Setting thread %d runnable\n", tid[i]); + thread_start (tid[i]); + thread_yield (); + } + mon_printf ("schedtest: Started %d threads\n", NUMTHREADS); + + while (1) { + mon_printf ("schedtest: Waiting for threads to complete\n"); + if (mon_tstc () && mon_getc () == 0x3) { + mon_printf ("schedtest: Aborting threads...\n"); + for (i = 0; i < NUMTHREADS; i++) { + mon_printf ("schedtest: Deleting thread %d\n", + tid[i]); + thread_delete (tid[i]); + } + return RC_SUCCESS; + } + j = -1; + i = thread_join (&j); + if (i == RC_FAILURE) { + mon_printf ("schedtest: No threads pending, " + "exiting schedular test\n"); + return RC_SUCCESS; + } + mon_printf ("schedtest: thread is %d returned %d\n", i, + j); + thread_yield (); + } + + return RC_SUCCESS; +} + +static int testthread (void *name) +{ + int i; + + mon_printf ("testthread: Begin executing thread, myname %d, &i=0x%08x\n", + *(int *) name, &i); + + mon_printf ("Thread %02d, i=%d\n", *(int *) name); + + for (i = 0; i < 0xffff * (*(int *) name + 1); i++) { + if (mon_tstc () && mon_getc () == 0x3) { + mon_printf ("testthread: myname %d terminating.\n", + *(int *) name); + return *(int *) name + 1; + } + + if (i % 100 == 0) + thread_yield (); + } + + mon_printf ("testthread: returning %d, i=0x%x\n", + *(int *) name + 1, i); + + return *(int *) name + 1; +} + + +static void sched_init (void) +{ + int i; + + for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) + lthreads[i].state = STATE_EMPTY; + + current_tid = MASTER_THREAD; + lthreads[current_tid].state = STATE_RUNNABLE; + DEBUG ("sched_init: master context = 0x%08x", + lthreads[current_tid].context); + return; +} + +static void thread_yield (void) +{ + static int i; + + DEBUG ("thread_yield: current tid=%d", current_tid); + +#define SWITCH(new) \ + if(lthreads[new].state == STATE_RUNNABLE) { \ + DEBUG("thread_yield: %d match, ctx=0x%08x", \ + new, lthreads[current_tid].context); \ + if(setjmp(lthreads[current_tid].context) == 0) { \ + current_tid = new; \ + DEBUG("thread_yield: tid %d returns 0", \ + new); \ + longjmp(lthreads[new].context, 1); \ + } else { \ + DEBUG("thread_yield: tid %d returns 1", \ + new); \ + return; \ + } \ + } + + for (i = current_tid + 1; i < MAX_THREADS; i++) { + SWITCH (i); + } + + if (current_tid != 0) { + for (i = 0; i <= current_tid; i++) { + SWITCH (i); + } + } + + DEBUG ("thread_yield: returning from thread_yield"); + return; +} + +static int thread_create (int (*func) (void *), void *arg) +{ + int i; + + for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) { + if (lthreads[i].state == STATE_EMPTY) { + lthreads[i].state = STATE_STOPPED; + lthreads[i].func = func; + lthreads[i].arg = arg; + DEBUG ("thread_create: returns new tid %d", i); + return i; + } + } + + DEBUG ("thread_create: returns failure"); + return RC_FAILURE; +} + +static int thread_delete (int id) +{ + if (id <= MASTER_THREAD || id > MAX_THREADS) + return RC_FAILURE; + + if (current_tid == id) + return RC_FAILURE; + + lthreads[id].state = STATE_EMPTY; + return RC_SUCCESS; +} + +static void thread_launcher (void) +{ + DEBUG ("thread_launcher: invoking func=0x%08x", + lthreads[current_tid].func); + + lthreads[current_tid].retval = + lthreads[current_tid].func(lthreads[current_tid].arg); + + DEBUG ("thread_launcher: tid %d terminated", current_tid); + + lthreads[current_tid].state = STATE_TERMINATED; + thread_yield (); + mon_printf ("thread_launcher: should NEVER get here!\n"); + + return; +} + +static int thread_start (int id) +{ + DEBUG ("thread_start: id=%d", id); + if (id <= MASTER_THREAD || id > MAX_THREADS) { + return RC_FAILURE; + } + + if (lthreads[id].state != STATE_STOPPED) + return RC_FAILURE; + + if (setjmp (lthreads[current_tid].context) == 0) { + lthreads[id].state = STATE_RUNNABLE; + current_tid = id; + DEBUG ("thread_start: to be stack=0%08x", lthreads[id].stack); + setctxsp (<hreads[id].stack[STK_SIZE]); + thread_launcher (); + } + + DEBUG ("thread_start: Thread id=%d started, parent returns", id); + + return RC_SUCCESS; +} + +#if 0 /* not used so far */ +static int thread_stop (int id) +{ + if (id <= MASTER_THREAD || id >= MAX_THREADS) + return RC_FAILURE; + + if (current_tid == id) + return RC_FAILURE; + + lthreads[id].state = STATE_STOPPED; + return RC_SUCCESS; +} +#endif /* not used so far */ + +static int thread_join (int *ret) +{ + int i, j = 0; + + DEBUG ("thread_join: *ret = %d", *ret); + + if (!(*ret == -1 || *ret > MASTER_THREAD || *ret < MAX_THREADS)) { + DEBUG ("thread_join: invalid tid %d", *ret); + return RC_FAILURE; + } + + if (*ret == -1) { + DEBUG ("Checking for tid = -1"); + while (1) { + /* DEBUG("thread_join: start while-loopn"); */ + j = 0; + for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) { + if (lthreads[i].state == STATE_TERMINATED) { + *ret = lthreads[i].retval; + lthreads[i].state = STATE_EMPTY; + /* DEBUG("thread_join: returning retval %d of tid %d", + ret, i); */ + return RC_SUCCESS; + } + + if (lthreads[i].state != STATE_EMPTY) { + DEBUG ("thread_join: %d used slots tid %d state=%d", + j, i, lthreads[i].state); + j++; + } + } + if (j == 0) { + DEBUG ("thread_join: all slots empty!"); + return RC_FAILURE; + } + /* DEBUG("thread_join: yielding"); */ + thread_yield (); + /* DEBUG("thread_join: back from yield"); */ + } + } + + if (lthreads[*ret].state == STATE_TERMINATED) { + i = *ret; + *ret = lthreads[*ret].retval; + lthreads[*ret].state = STATE_EMPTY; + DEBUG ("thread_join: returing %d for tid %d", *ret, i); + return RC_SUCCESS; + } + + DEBUG ("thread_join: thread %d is not terminated!", *ret); + return RC_FAILURE; +} -- cgit v1.2.3