head 4.4; access ; symbols ; locks oasisa:4.4; strict; comment @ * @; 4.4 date 2001.06.19.12.15.47; author oasisa; state Exp; branches ; next ; desc @New Repository; 6/19/2001 (klh) @ 4.4 log @New Repository; 6/19/2001 (klh) @ text @/************************************************************************/ /* Copyright 1991 MBARI */ /************************************************************************/ /* $Header: task.c,v 1.1 2001/06/19 11:45:01 oasisa Exp $ */ /* Summary : Multitasking library for 80C196 */ /* Filename : task.c */ /* Author : Robert Herlien (rah) */ /* Project : OASIS Mooring */ /* $Revision: 1.1 $ */ /* Created : 06/05/91 */ /* */ /* MBARI provides this documentation and code "as is", with no warranty, */ /* express or implied, of its quality or consistency. It is provided without*/ /* support and without obligation on the part of the Monterey Bay Aquarium */ /* Research Institute to assist in its use, correction, modification, or */ /* enhancement. This information should not be published or distributed to */ /* third parties without specific written permission from MBARI. */ /* */ /************************************************************************/ /* Modification History: */ /* 05jun91 rah - created */ /* $Log: task.c,v $ * Revision 1.1 2001/06/19 11:45:01 11:45:01 oasisa (Oasis users) * Initial revision * * Revision 4.1 98/05/12 09:35:07 09:35:07 bobh (Bob Herlien) * June '98 turnaround for EqPac * * Revision 3.8 97/09/12 10:50:48 10:50:48 bobh (Bob Herlien) * Redeploy M1 * * Revision 3.4 96/06/18 15:24:12 15:24:12 bobh (Bob Herlien) * June '96 deployment of M1 * * Revision 3.1 95/03/09 19:30:52 19:30:52 hebo (Bob Herlien) * March '95 Deployment of M1A * * Revision 3.0 95/02/21 18:42:38 18:42:38 hebo (Bob Herlien) * February '95 Deployment * * Revision 2.4 93/10/29 11:12:42 11:12:42 hebo (Bob Herlien) * November 1993 Deployment * * Revision 2.0 92/08/21 14:45:54 14:45:54 hebo (Bob Herlien) * August 1992 deployment * * Revision 1.3 92/03/03 16:41:25 16:41:25 hebo (Bob Herlien 408-647-3748) * New defaults, restart check, perm power stuff, analog command * */ /************************************************************************/ /* Note - This is a very simple, NON-PREEMPTIVE multitasking library for*/ /* the 80C196 family of microcontrollers. Note in particular that it */ /* is non-preemptive. When your task has the CPU, it keeps it until */ /* you task_exit(), dispatch(), task_delay(), or sem_take(). The benefit*/ /* is a simpler programming model. You don't have to worry about */ /* critical regions -- just don't call dispatch() until you're done with*/ /* a critical resource. The down side is that latency and throughput */ /* depend on the cooperation of all tasks. The intent is that YOU, in */ /* your application code, decide on a convention for how long it's */ /* reasonable for a task to hold onto the CPU, and then you enforce */ /* that policy in each task that you write. */ /* */ /* This was written for the OASIS mooring controller. In OASIS, the */ /* convention is that each task will relinquish the CPU at least once */ /* per clock tick, and that the clock ticks at 100 Hz (10 ms). */ /* */ /* The Intel C96 compiler makes no distinction between registers used */ /* as globals and local (automatic) register variables. Consequently, */ /* in order to prevent tasks from stepping on each others locals, we */ /* must (a) compile everyting with the REGCONSERVE keyword, and (b) not */ /* use register variables in functions. (Actually, register variables */ /* are usable if you're *sure* that neither the function it's in, nor */ /* any function it calls, will dispatch another task. The conservative */ /* rule, though, is NO register variables except globals). */ /************************************************************************/ #include /* MBARI type definitions */ #include /* MBARI constants */ #include /* OASIS controller definitions */ #include /* Needed for default serial port */ #include /* Task library definitions */ #include <80c196.h> /* 80196 Register mapping */ #include /* String library functions */ /********************************/ /* External Functions */ /********************************/ Extern char *tmpMalloc( Nat16 size ); Extern Void tmpFree( char *ptr ); Extern Void do_dispatch( TaskDesc *td ); Extern Void bzero( void *s, int n ); /********************************/ /* External Data */ /********************************/ Extern Reg FuncPtr *SP; /* Stack Pointer */ Extern FuncPtr STACK; /* Linker's STACK segment */ Extern Reg Nat16 PLMREG; /* Stack Pointer */ /********************************/ /* Global Data */ /********************************/ Global Reg Int16 task_tick; /* Ticks since last dispatch() */ Global Reg Nat16 tasks; /* Number of tasks that are active */ /********************************/ /* Module Local Data */ /********************************/ MLocal Reg LstHead ready_list; /* Ready list */ MLocal Reg LstHead delay_list; /* Delay list */ /************************************************************************/ /* Function : ready */ /* Purpose : Put task on ready list */ /* Inputs : TaskDesc of task to make ready */ /* Outputs : None */ /************************************************************************/ Void ready( Reg TaskDesc *td ) { td->td_state = READY; /* Make task ready */ list_add( &ready_list, (Node *)td ); /* Put on ready list */ } /* ready() */ /************************************************************************/ /* Function : task_get */ /* Purpose : Return Tid of running task */ /* Inputs : None */ /* Outputs : Task Descriptor */ /************************************************************************/ TaskDesc * task_get( Void ) { return( (TaskDesc *)(ready_list.lst_head) ); } /* task_get() */ /************************************************************************/ /* Function : unready */ /* Purpose : Take running task off ready list, return its Tid */ /* Inputs : None */ /* Outputs : Task ID */ /************************************************************************/ TaskDesc * unready( Void ) { return( (TaskDesc *)list_head(&ready_list) ); } /* unready() */ /************************************************************************/ /* Function : ready_delayed_tasks */ /* Purpose : Decrement delay count of tasks on delay list, put */ /* ready ones on ready_list */ /* Inputs : None */ /* Outputs : None */ /* Comments : Instead of decrementing delay counts during the clock */ /* tick interrupt, which would require mutual exclusion */ /* on both the delay_list and ready_list, we do the */ /* following. The tick interrupt simply increments global*/ /* variable task_tick. This function, which actually */ /* manages the delay list, is called at dispatch time from*/ /* function do_dispatch(). It uses task_tick to determine*/ /* how many ticks expired since the last time it ran, and */ /* decrements all delay counts by that amount. It then */ /* zeroes task_tick. This, and the fact that dispatching */ /* is non-preemptive, ensure mutual exclusion on the kernel*/ /* lists without explicit semaphores or interrupt-off regions*/ /************************************************************************/ Void ready_delayed_tasks( Void ) { Reg Node *np, *nnp; /* Current, next task */ Reg Int16 count; /* Ticks since dispatch() */ count = task_tick; /* Get ticks since last time*/ task_tick -= count; /* Zero tick counter */ if ( count == 0 ) /* If no ticks, return */ return; for( np = delay_list.lst_head; np != NULLNODE; ) { /* For each task on dlay lst*/ nnp = np->lst_next; if ( (((TaskDesc *)np)->td_delay -= count) <= 0 ) { /* if delay done, */ list_get( &delay_list, np ); /* take task off delay list */ ready( (TaskDesc *)np ); /* and put on ready list */ } np = nnp; } } /* ready_delayed_tasks() */ /************************************************************************/ /* Function : dispatch */ /* Purpose : Application-level function to dispatch a task */ /* Inputs : None */ /* Outputs : None */ /* Comments : Puts current task at end of ready list, dispatches */ /* task at head of ready list */ /************************************************************************/ Void dispatch( Void ) { Reg TaskDesc *td; /* TD of running task */ ready_delayed_tasks(); /* Take ready tasks off delay list*/ td = unready(); /* Get our Task Descriptor */ ready( td ); /* Put task at end of ready list */ do_dispatch( td ); /* Call ass'y language dispatcher */ } /* dispatch() */ /************************************************************************/ /* Function : task_delay */ /* Purpose : Delay a number of ticks */ /* Inputs : Number of ticks to delay */ /* Outputs : None */ /* Comments : Because delay-list management can result in decrementing*/ /* delays by multiple counts (see comments in function */ /* ready_delayed_tasks(), above), the delay count must be */ /* a signed integer. So count is limited to a positive */ /* integer, or 32767. */ /************************************************************************/ Void task_delay( Int16 count ) { Reg TaskDesc *td; /* No disable(), non-preemptive */ td = unready(); /* Get our TaskDesc */ td->td_delay = count + task_tick; /* Add ticks already past to dly cnt*/ td->td_state = DELAY; /* Set delay state */ list_add( &delay_list, (Node *)td ); /* Put task on delay_list */ do_dispatch( td ); /* Dispatch new task */ } /* task_delay() */ /************************************************************************/ /* Function : delay_secs */ /* Purpose : Delay a number of secondss */ /* Inputs : Number of seconds to delay */ /* Outputs : None */ /************************************************************************/ Void delay_secs( Nat16 secs ) { Nat16 i; for( i = secs; i; i-- ) task_delay( TICKS_PER_SECOND ); } /* delay_secs() */ /************************************************************************/ /* Function : task_exit */ /* Purpose : Kill a task */ /* Inputs : None */ /* Outputs : None */ /************************************************************************/ Void task_exit( Void ) { Reg TaskDesc *td; /* Our own task descriptor */ Reg Driver *dp; td = unready(); /* Take task off ready list */ dp = td->td_drvr; /* Get driver ptr */ dp->drv_td = NULLTID; /* Show driver not running */ if ( (dp->drv_flags & FLAG_PERM) == 0 ) dp->drv_flags = 0; tmpFree( (char *)td ); /* Free task descriptor & stack */ if ( --tasks == 0 ) /* Decrement task count */ asm rst; /* Should never go to zero */ do_dispatch( NULLTASK ); /* Dispatch next task */ } /* task_exit() */ /************************************************************************/ /* Function : task_alloc */ /* Purpose : Allocate TaskDesc and stack for a new task */ /* Inputs : Stack size */ /* Outputs : Task Descriptor */ /************************************************************************/ TaskDesc * task_alloc( Nat16 stksize ) { Reg TaskDesc *td; Reg Nat16 size; size = sizeof(TaskDesc) + stksize; if ( (td = (TaskDesc *)tmpMalloc(size)) == NULLTASK ) return( NULLTASK ); /* Alloc TaskDesc, fail if no space*/ bzero((void *)td, sizeof(TaskDesc)); /* Clear task descriptor */ if ( stksize > 0 ) /* Fill stack with RST instrucs */ memset( (void *)(td + 1), 0xff, stksize ); td->td_sem = NULLSEM; /* Init sem ptr */ td->td_state = READY; /* Init state */ td->td_stack = (FuncPtr *)((Byte *)td + size); return( td ); } /* task_alloc() */ /************************************************************************/ /* Function : task_create */ /* Purpose : Create a task */ /* Inputs : Driver pointer */ /* Outputs : None */ /************************************************************************/ TaskDesc * task_create( Reg Driver *dp ) { Reg TaskDesc *td; Reg FuncPtr *taskSP; if ( (td = task_alloc(STKSIZE)) == NULLTASK ) return( NULLTASK ); /* Alloc TaskDesc, fail if no space*/ td->td_name = dp->drv_name; /* Insert name pointer */ /* Set up serial parameters */ td->td_drvr = dp; /* Save driver ptr */ /* Now run the task */ taskSP = td->td_stack; /* Initialize task stack pointer*/ *--taskSP = (FuncPtr)dp; /* Push driver ptr as calling arg*/ *--taskSP = task_exit; /* Push a return to exit func */ *--taskSP = dp->drv_task; /* Push program entry ptr */ *--taskSP = (FuncPtr)dp->drv_parms[SER_PORT]; *--taskSP = (FuncPtr)dp->drv_parms[SER_SETUP]; *--taskSP = (FuncPtr)SP; /* Dummy push SP replaces ?FRAME01*/ td->td_sp = taskSP; /* Store task stack pointer */ ready( td ); /* Put task on ready list */ tasks++; /* Increment task count */ return( td ); /* Return td */ } /* task_create() */ /************************************************************************/ /* Function : task_init */ /* Purpose : Initialize Multitasking library */ /* Inputs : Pointer to valid TaskDesc for calling task */ /* Outputs : None */ /* Comments : MUST be first call to multitasking library */ /* Makes the calling routine into the initial task, using */ /* the stack allocated by linker as calling task's stack */ /************************************************************************/ TaskDesc * task_init( Void ) { Reg TaskDesc *td; tasks = 1; task_tick = 0; list_init( &ready_list ); list_init( &delay_list ); if ( (td = task_alloc(0)) != NULLTASK ) { td->td_name = "Init"; td->td_stack = &STACK; ready( td ); } return( td ); } /* task_init() */ @