00001 #ifndef _OS_H_ 00002 #define _OS_H_ 00003 /** 00004 * \file os.h 00005 * \brief A simple RTOS interface 00006 * 00007 * \mainpage A Simple RTOS 00008 * This is a simple RTOS that supports pre-emptive multithreading, and 00009 * interprocess synchronization using Events. 00010 * 00011 * \b Note: Please don't edit the interface file "os.h". 00012 * 00013 * \author Dr. Mantis Cheng 00014 * \date 26 September 2007 00015 * 00016 * \section assumptions GLOBAL ASSUMPTIONS 00017 * 00018 * (ATMEL specific) 00019 * - Counter1 Timer and SWI interrupts are reserved. 00020 * 00021 * - All runtime exceptions (where assumptions are violated) or other 00022 * unrecoverable errors get handled by calling OS_Abort(). 00023 * - Each valid entry in PPP[] must be non-zero except IDLE. 00024 * - All unspecified runtime errors have undefined behaviours, e.g., stack overflow. 00025 * - PPP[] \b cannot be modified once the application starts running. 00026 * 00027 * \section policy SCHEDULING POLICY 00028 * 00029 * There are three scheduling levels: SYSTEM, PERIODIC and RR. 00030 * These levels are prioritized with SYSTEM being the highest, and RR being 00031 * the lowest. 00032 * 00033 * Preemption occurs immediately. Whenever preemption is feasible, it takes 00034 * place instantly. As soon as a higher priority task becomes ready, it 00035 * preempts all lower priority tasks. 00036 * 00037 * \section system SYSTEM TASKS 00038 * 00039 * SYSTEM (level) tasks are FCFS; they run to completion, i.e., until they 00040 * terminate, block or yield. Thus, they are non-preemptible, not even by 00041 * other SYSTEM tasks. They should only be used for critical system level 00042 * activities, e.g., error or fault recovery. Running too many SYSTEM tasks 00043 * could affect the real time performance of all other low level tasks. 00044 * 00045 * 00046 * \section periodic PERIODIC TASKS 00047 * 00048 * PERIODIC tasks are scheduled based on a fixed cyclic scheduling plan; 00049 * they are time-based. (See below about PPP[].) Since they are time-critical, 00050 * they are \b NOT allowed to block (i.e., wait on an event). 00051 * When a PERIODIC task is created, it is assigned a "name", 00052 * a non-zero user-defined constant between 1 and MAXPROCESS. 00053 * This name is fixed and can NEVER be changed again. 00054 * No two PERIODIC tasks have the same name. All names are unique. 00055 * Since tasks may terminate and be created again over time, 00056 * the same name may be reused over time for different PERIODIC task 00057 * instance. 00058 * 00059 * The PPP[] array is essentially a linear representation of a Gantt diagram. 00060 * It is an array of [Name1, Interval1, Name2, Interval 2, ... ]. 00061 * The name of every PERIODIC task must appear in PPP[] array at least once, 00062 * but may be more than once. 00063 * 00064 * For example, if we create three PERIODIC tasks with names A, B and 00065 * C out of three functions P(), Q() and R() respectively. Then, 00066 * PPP[] = { A, 2, B, 3, A, 5, C, 1 } means executing A for 2 ticks, 00067 * then B for 3 ticks, then A again for 5 ticks, then C for 1 tick, 00068 * then A again, and so on. The total cycle time is 2+3+5+1=11 ticks. 00069 * That is, within 11 ticks, A executes twice, B once and C once. 00070 * If P() terminates, but the name A is later assigned to another function U(), 00071 * then A will be executed again according to PPP[] order using U(). 00072 * In a sense, PPP[] specifies at least a single execution cycle 00073 * of all PERIODIC tasks. IDLE is a special PERIODIC task name, which means 00074 * do nothing during this task's assigned interval. 00075 * 00076 * A PERIODIC task may yield (calling Task_Next()) to relinquish 00077 * the processor before its assigned interval. In this case, it has completed 00078 * its current execution interval and is waiting for its next interval. 00079 * 00080 * It is a runtime error if a PERIODIC task executes longer than the 00081 * currently assigned interval. It is important NOT to underestimate 00082 * the execution time requirement of a PERIODIC task. Choosing the appropriate 00083 * execution order and intervals for all PERIODIC tasks is the responsibility 00084 * of the Application Design Engineer(s), not our RTOS. Hence, all timing 00085 * violations should be caught and then reported. 00086 * 00087 * By specifying PPP[] and scheduling our PERIODIC tasks accordingly, 00088 * we shall know precisely the execution "cycle" time of all such tasks, 00089 * thus their best execution frequency/rate and response time. This is how 00090 * we guarantee the predictability of timing and ordering all critical 00091 * activities. 00092 * 00093 * \section rr RR TASKS 00094 * 00095 * RR tasks are scheduled in a round-robin fashion, i.e., each RR 00096 * task runs for one TICK approximately and yields to the next RR task. They 00097 * don't have any time critical schedule to follow, thus they share the 00098 * processor cycles fairly. 00099 * 00100 * RR tasks are allowed to run \a only when no PERIODIC or SYSTEM tasks 00101 * are executing. When an RR task resumes after pre-emption, 00102 * it re-enters its RR level at the end. When an RR task 00103 * yields, or resumes after being unblocked, it re-enters its level 00104 * at the end as well. 00105 * 00106 * 00107 * \section boot OS BOOTING 00108 * Our RTOS is compiled together with an application. It doesn't provide 00109 * a "main()" function, which is a part of the application. By convention, 00110 * the "main()" is the first function to be called by the C runtime code, 00111 * "crt0.S". For our RTOS, we shall change this convention as follows: 00112 * -# OS_Init() is called from crt0.S as the very first C function to be 00113 * executed instead of main(). 00114 * -# Upon completion of OS_Init(), the application's main() is then 00115 * created as the first and only SYSTEM level task. 00116 * -# In main(), the rest of the application tasks are then created. 00117 * -# In order for all other application tasks to run, our main() task 00118 * must either terminate or block on an event. (For example, main() 00119 * may become a "watchdog" task to reset the entire application.) 00120 * 00121 * 00122 * \section ipc INTERPROCESS COMMUNICATION 00123 * 00124 * Events are one-way synchronization signals. They don't have any "memory" 00125 * (i.e., values); thus, they are NOT semaphores. Any SYSTEM or RR task may 00126 * wait on an event; PERIODIC tasks \b MUST \b NOT wait on any event. 00127 * However, any task may signal/broadcast an event. 00128 * A waiting task is resumed when the associated event is signalled. 00129 * All waiting tasks are resumed when the associated event is broadcasted. 00130 * When an event is signalled (or broadcasted) but there are no waiting tasks, 00131 * this is a \a no-op; hence, events have \b no memory. 00132 * 00133 */ 00134 00135 00136 /*================================================================== 00137 * T Y P E S & C O N S T A N T S 00138 *================================================================== 00139 */ 00140 00141 /*================ 00142 * C O N S T A N T S 00143 *================ 00144 */ 00145 /* limits */ 00146 00147 /** max. number of processes supported */ 00148 #define MAXPROCESS 8 00149 00150 /** max. number of Events supported */ 00151 #define MAXEVENT 8 00152 00153 /** workspace size of each process in bytes */ 00154 #define WORKSPACE 256 00155 00156 /** milliseconds, or something close to this value 00157 * \sa \ref periodic. 00158 */ 00159 #define TICK 5 00160 00161 /* scheduling levels */ 00162 00163 /** a scheduling level: system tasks with first-come-first-served policy 00164 * \sa \ref system, Task_Create(). 00165 */ 00166 #define SYSTEM 3 00167 00168 /** a scheduling level: periodic tasks with predefined intervals 00169 * \sa \ref periodic, Task_Create(). 00170 */ 00171 #define PERIODIC 2 00172 00173 /** A scheduling level: first-come-first-served cooperative tasks 00174 * \sa \ref rr, Task_Create(). 00175 */ 00176 #define RR 1 00177 00178 #define NULL 0 /* undefined */ 00179 #define IDLE 0 /* a well-known PERIODIC task name */ 00180 00181 /*================ 00182 * T Y P E S 00183 *================ 00184 */ 00185 /** An Event descriptor 00186 * \sa Event_Init(). 00187 */ 00188 typedef struct event EVENT; 00189 00190 /*================ 00191 * G L O B A L S 00192 *================ 00193 */ 00194 /** 00195 * A periodic task scheduling plan (read-only, defined by the application). 00196 * 00197 * This is specified by the application. The scheduling ordering and duration 00198 * of all PERIODIC tasks are specified by this plan. The total number of 00199 * occurrences of all PERIODIC tasks, including IDLE, is given by PT. 00200 * e.g., PPP[] = { A, 2, IDLE, 5, B, 1, A, 2, C, 4 }; 00201 * PT = 5; 00202 * \sa \ref periodic. 00203 */ 00204 extern const unsigned char PPP[]; 00205 extern const unsigned int PT; 00206 00207 /*================================================================== 00208 * A C C E S S P R O C E D U R E S 00209 *================================================================== 00210 */ 00211 00212 /*===== OS Initialization ===== */ 00213 00214 /** Initialize this RTOS; must be called first. 00215 * \sa \ref boot. 00216 */ 00217 void OS_Init(void); 00218 00219 /** Abort the execution of this RTOS due to an unrecoverable erorr. 00220 * \sa \ref assumptions. 00221 */ 00222 void OS_Abort(void); 00223 00224 /*===== Task API ===== */ 00225 00226 /** 00227 * \param f a parameterless function to be created as a process instance 00228 * \param arg an integer argument to be assigned to this process instanace 00229 * \param level assigned scheduling level: SYSTEM, PERIODIC or RR 00230 * \param name assigned PERIODIC process name 00231 * \return 0 if not successful; otherwise non-zero. 00232 * \sa Task_GetArg(), PPP[]. 00233 * 00234 * A new process is created to execute the parameterless 00235 * function \a f with an initial parameter \a arg, which is retrieved 00236 * by a call to Task_GetArg(). If a new process cannot be 00237 * created, 0 is returned; otherwise, it returns non-zero. 00238 * The created process will belong to its scheduling \a level. 00239 * If the process is PERIODIC, then its \a name is a user-specified name 00240 * to be used in the PPP[] array. Otherwise, \a name is ignored. 00241 * \sa \ref policy 00242 */ 00243 int Task_Create(void (*f)(void), int arg, unsigned int level, unsigned int name); 00244 00245 /** 00246 * Terminate the calling process 00247 * 00248 * When a process returns, i.e., it executes its last instruction in 00249 * the associated function/code, it is automatically terminated. 00250 */ 00251 void Task_Terminate(void); 00252 00253 /** Voluntarily relinquish the processor. */ 00254 void Task_Next(void); 00255 00256 /** Retrieve the assigned parameter. 00257 * \sa Task_Create(). 00258 */ 00259 int Task_GetArg(void); 00260 00261 /*===== Events API ===== */ 00262 00263 /** 00264 * \return a non-NULL Event descriptor if successful; NULL otherwise. 00265 * 00266 * Initialize a new, non-NULL Event descriptor. 00267 */ 00268 EVENT *Event_Init(void); 00269 00270 /** 00271 * \param e an Even descriptor 00272 * 00273 * Wait for the next occurrence of a signal on \a e. The calling process always blocks. 00274 */ 00275 void Event_Wait( EVENT *e ); 00276 00277 /** 00278 * \param e an Event descriptor 00279 * 00280 * Resume a \b single waiting task on \a e. It is a \a no-op if there is no waiting process. 00281 * \sa Event_Wait() 00282 */ 00283 void Event_Signal( EVENT *e ); 00284 00285 /** 00286 * \param e an Event descriptor 00287 * 00288 * Resume \b ALL waiting tasks on \a e. It is a \a no-op if there is no waiting process. 00289 * \sa Event_Wait() 00290 */ 00291 void Event_Broadcast( EVENT *e ); 00292 00293 00294 /** 00295 * \param e an Event descriptor 00296 * 00297 * Resume a waiting task on \a e and at the same time relinquish the processor. 00298 * This is equivalent to "Event_Signal( e ); Task_Next()" in concept. The 00299 * fundamental difference is that these two operations are performed as 00300 * an indivisible unit. So conceptually, the calling task resumes another 00301 * waiting task and gives up its share of the processor simultaneously. 00302 * \sa Event_Signal(), Task_Next() 00303 */ 00304 void Signal_And_Next( EVENT *e ); 00305 00306 /** 00307 * \param e an Event descriptor 00308 * 00309 * Resume \b ALL waiting tasks on \a e and at the same time relinquish the processor. 00310 * This is equivalent to "Event_Broadcast( e ); Task_Next()" in concept. 00311 * \sa Event_Broadcast(), Task_Next() 00312 */ 00313 void Broadcast_And_Next( EVENT *e ); 00314 00315 #endif /* _OS_H_ */