Subversion Repositories Bart

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 mjames 1
/* RCS revision control
2
   $Header: c:/cvsroot/bart/rt_task.c,v 1.4 2004/03/09 22:09:10 mjames Exp $
3
 */
4
 
5
/* RCS Log file
6
 
7
   $Log: rt_task.c,v $
8
   Revision 1.4  2004/03/09 22:09:10  mjames
9
   Hardware flow control implemented
10
 
11
   Revision 1.3  2004/03/09 00:45:20  mjames
12
   Corrected mistakes, made task numbers visible
13
 
14
   Revision 1.2  2004/03/06 12:17:48  mjames
15
   Moved headers around, made it clearer that there are no configurable
16
   parts to the OS unless it is rebuilt
17
 
18
   Revision 1.1.1.1  2004/03/03 22:54:33  mjames
19
   no message
20
 
21
 */
22
 
23
 
24
 
25
/*******************
26
*  INCLUDE FILES   *
27
********************/
28
#include "mcs51reg.h"
29
 
30
#include "rt_int.h"
31
#include "rt_ext.h"
32
 
33
 
34
/*******************
35
* LOCAL MACROS     *
36
********************/
37
 
38
 
39
/** compute timer rolling over for T0 */
40
#define ROLLOVER_RATE (PRESCALE2/8192)
41
/* this is 112.5 Hz */
42
 
43
#define INT_TASK_BIT(tasknum) (1<<(tasknum))
44
 
45
/* this code is for the main uart of the 8051 */
46
/* timer registers used by T0 IRQ */
47
/* 11.25Hz counter bank */
48
volatile unsigned char T0ctr;
49
 
50
/* this 'register' is exported */
51
volatile unsigned char T100ms;
52
 
53
 
54
volatile unsigned char T10sec;
55
 
56
/*  first idata */
57
STACK_TYPE  stack0[STACK0SIZE];
58
STACK_TYPE  stack1[STACK1SIZE];
59
#if TASKS >=3
60
STACK_TYPE stack2[STACK2SIZE];
61
#endif
62
#if TASKS >=4
63
STACK_TYPE stack3[STACK3SIZE];
64
#endif
65
volatile STACK_PTR_TYPE stack_save[TASKS];
66
volatile SIGNAL_TYPE    task_signals[TASKS];
67
volatile SIGNAL_TYPE    task_masks[TASKS];
68
volatile TIMER_TYPE     task_timer[TASKS];
69
volatile TASKID_TYPE    ready ;
70
volatile TASKID_TYPE    run  ;
71
 
72
 
73
 
74
/* almost round robin with a modulo-4 task counter */
75
#define RUN_TOG  0x10
76
#define RUN_MASK 0x3F
77
 
78
/* 3 tasks including idle */
79
#if TASKS == 3
80
const STACK_PTR_TYPE  start_stack[]={
81
  stack0,
82
  stack1,
83
  stack2};      
84
 
85
 
86
#define VALID_TASK_NUM
87
#endif
88
 
89
#if TASKS == 4
90
const STACK_PTR_TYPE start_stack[]={
91
  stack0,
92
  stack1,
93
  stack2,
94
  stack3};      
95
 
96
 
97
#define VALID_TASK_NUM
98
#endif
99
 
100
#if !defined VALID_TASK_NUM
101
#error Invalid TASK_NUM declaration
102
#endif
103
 
104
/** This task table describes a round robin with priority :
105
    there are 4 phases to the round robin
106
    regardless of the number of tasks , so one task wins more often
107
    with 3 tasks  tasks 1 and 2 are high priority,
108
   task 3 runs when 1 and 2 are not running. If there are less than 4 tasks then
109
   this table will still apply . */
110
const char priotab[] =
111
  {
112
                /** round 00 : leftmost task bit wins*/
113
                /*T3210 */
114
     0,0,1,1,   /**< 0000,0001,0010,0011 */
115
     2,2,2,2,   /**< 0100,0101,0110,0111 */
116
     3,3,3,3,   /**< 1000,1001,1010,1011 */
117
     3,3,3,3,   /**< 1100,1101,1110,1111 */
118
                /** round 01 : second task bit wins */
119
                /*T3210 */
120
     0,0,1,0,   /**< 0000,0001,0010,0011 */
121
     2,0,1,1,   /**< 0100,0101,0110,0111 */
122
     3,0,1,1,   /**< 1000,1001,1010,1011 */
123
     2,2,2,2,   /**< 1100,1101,1110,1111 */
124
                /** round 10 : third task bit wins */
125
                /*T3210 */
126
     0,0,1,1,   /**< 0000,0001,0010,0011 */
127
     2,2,2,0,   /**< 0100,0101,0110,0111 */
128
     3,3,3,0,   /**< 1000,1001,1010,1011 */
129
     3,0,1,1,   /**< 1100,1101,1110,1111 */
130
                /** round 11 : fourth or cycle again */
131
                /*T3210 */
132
     0,0,1,0,   /**< 0000,0001,0010,0011 */
133
     2,0,1,2,   /**< 0100,0101,0110,0111 */
134
     3,0,1,3,   /**< 1000,1001,1010,1011 */
135
     2,3,3,0,   /**< 1100,1101,1110,1111 */
136
  };
137
 
138
 
139
 
140
/** Sets up the scheduler state variables to a clean initial state, but indicate a
141
  task identified as MAIN_TASK_ID (the task under which main() will run)
142
  is actually running and ready to run. Use with EA off !!*/
143
void rt_tasks_init (void)
144
  {
145
  char i;
146
  run   = MAIN_TASK_ID;
147
  ready = TASK_BIT(MAIN_TASK_ID);
148
 
149
  for(i=0;i<TASKS;i++)
150
    {
151
    stack_save[i] = 0;
152
    task_signals[i]=0;
153
    task_masks[i]  =0;
154
    task_timer[i]  =0;
155
    }
156
 
157
}
158
 
159
 
160
 
161
 
162
 
163
/** the 100ms counter is one every 11.25 timeouts of the T0 counter    
164
  * so we will count 12 timeouts when T100ms MOD 4 = 0 and 11 otherwise
165
  * this means that 10 counts of T100ms are almost exactly 1 second     */
166
 
167
/** Timer interrupt running about once every 10ms */
168
bit T0_interrupt_ea;
169
 
170
void T0Interrupt(void) interrupt T0_INTVEC using T0_INTERRUPT_BANK
171
  {
172
/*  TF0 = 0;  This Interrupt is cleared automatically by jumping down this vector */
173
  T0ctr--;
174
  if(T0ctr==0)
175
    {
176
    int i;
177
    T100ms++;
178
    if(T100ms & 3)
179
      T0ctr= 11;/* 01,10,11  */
180
    else
181
      T0ctr= 12;/* 00 */
182
/* and now decrement all of the 100 ms timers */
183
    for (i=0;i<TASKS;i++)
184
      {
185
      if (task_timer[i] != 0)
186
        {
187
        task_timer[i]--;
188
        if (task_timer[i] == 0)
189
          {
190
          task_signals[i] |= TIMER_SIG;
191
          if(task_signals[i] & task_masks[i])
192
            {
193
            ready |= TASK_BIT(i); /* Mapping here */  
194
            }
195
          }
196
        }
197
      }    
198
/* wrap T100ms around an integer number of seconds
199
   The wrap value should be divisible by 4 because of
200
   the fractional N counting */
201
    if(T100ms>=MAXT100ms)
202
      {
203
      T10sec++;
204
      T100ms=0;  
205
      }
206
    }
207
/* use this interrupt to repetitively sample the flow
208
   control lines for the primary UART */
209
#if defined HARD_FLOW
210
   /* if nCTS goes low and we are not watching it then retrigger TI */
211
  if(!SIO1_CTS && !SIO1_TxBusy && SIO1_TxCount)
212
      {
213
      TI=1;
214
      }
215
#endif
216
 
217
 
218
 
219
#if !defined NO_IRQ_SCHEDULE
220
 
221
 _asm
222
; use assembler form of atomic operation for keeping ea
223
  jbc ea,0001$  
224
  clr _T0_interrupt_ea
225
  sjmp 0002$
226
0001$:
227
  setb _T0_interrupt_ea
228
0002$:
229
 
230
 
231
 
232
; These are in bank 0
233
  push  7
234
  push  6
235
  push  5
236
  push  4
237
  push  3
238
  push  2
239
  push  1
240
  push  0
241
;
242
;
243
; Determine which task was running
244
; and save the task SP
245
;
246
  mov a,_run
247
  add a,#_stack_save
248
  mov r0,a
249
  mov a,sp
250
  mov @r0,a
251
 
252
; *********** SCHEDULE CODE HERE
253
;
254
; look at the task ready flags
255
  mov  a,_ready
256
  add  a,#RUN_TOG
257
  anl  a,#RUN_MASK
258
  mov  _ready,a
259
;
260
; get new running task ID
261
;
262
  mov  dptr,#_priotab  
263
  movc a,@a+dptr
264
;
265
  mov _run,a
266
;  
267
  add a,#_stack_save
268
  mov r0,a
269
  mov a,@r0
270
  mov sp,a
271
;
272
 
273
;
274
; Bank 0 registers in use here
275
;
276
  pop  0
277
  pop  1
278
  pop  2
279
  pop  3
280
  pop  4
281
  pop  5
282
  pop  6
283
  pop  7
284
;
285
; this is done in the ISR
286
;       pop     psw
287
;       pop     dph
288
;       pop     dpl
289
;       pop     b
290
;       pop     acc
291
;
292
;
293
;  
294
  jnb _T0_interrupt_ea,0010$
295
  setb ea
296
0010$:
297
  _endasm;
298
 
299
#endif
300
 
301
  }
302
 
303
/********************************************************************************
304
** Name :               reschedule
305
 This function determines which task to run, by using the ready variable as
306
an index into the scheduler table. Each call to reschedule increments the upper
307
2 bits of the ready variable, so as to use a different part of the table on each call.
308
This allows a round-robin with priority scheduling to take place.
309
\break
310
To set a bit in the ready variable, the task's signal (task_signal[task]) variable is
311
masked with the task's mask (task_masks[task]) variable. If the result is non-zero then the
312
bit corresponding to the tasks ID number is set in the ready variable.
313
\break
314
Task timers reaching zero from a non-zero value will assert the TIMER_SIG signal
315
in the task's signal variable. If the mask in task's masks has TIMER_SIG set at that time
316
then the task will have its bit set in the ready variable.
317
\break
318
A task will never run again if it has a zero task_masks variable and a zero task_timer
319
variable, and its ready bit is zero. In all other cases the task will run again in the
320
future
321
 
322
 
323
 
324
*********************************************************************************/
325
 
326
 
327
bit reschedule_ea;
328
void reschedule(void)
329
  {
330
  _asm
331
; use assembler form of atomic operation for keeping ea
332
  jbc ea,0001$  
333
  clr _reschedule_ea
334
  sjmp 0002$
335
0001$:
336
  setb _reschedule_ea
337
0002$:
338
 
339
; this is done in any ISR
340
        push    acc
341
        push    b
342
        push    dpl
343
        push    dph
344
        push    psw
345
 
346
;
347
; These are in bank 0
348
;
349
  push  7
350
  push  6
351
  push  5
352
  push  4
353
  push  3
354
  push  2
355
  push  1
356
  push  0
357
;
358
;
359
;
360
; Determine which task was running
361
; and save the task SP
362
;
363
  mov a,_run
364
  add a,#_stack_save
365
  mov r0,a
366
  mov a,sp
367
  mov @r0,a
368
 
369
; *********** SCHEDULE CODE HERE
370
;
371
; look at the task ready flags
372
  mov  a,_ready
373
  add  a,#RUN_TOG
374
  anl  a,#RUN_MASK
375
  mov  _ready,a
376
;
377
; get new running task ID
378
;
379
  mov  dptr,#_priotab  
380
  movc a,@a+dptr
381
;
382
  mov _run,a
383
;  
384
  add a,#_stack_save
385
  mov r0,a
386
  mov a,@r0
387
  mov sp,a
388
;
389
;
390
; Bank 0 registers in use here
391
;
392
  pop  0
393
  pop  1
394
  pop  2
395
  pop  3
396
  pop  4
397
  pop  5
398
  pop  6
399
  pop  7
400
 
401
;
402
; this is done in the ISR
403
        pop     psw
404
        pop     dph
405
        pop     dpl
406
        pop     b
407
        pop     acc
408
;
409
  jnb _reschedule_ea,0010$
410
  setb ea
411
0010$:
412
;
413
  _endasm;
414
 
415
  }
416
 
417
 
418
/********************************************************************************
419
** Name :               start_task
420
 * the stack for the task is built and then its run flag is set  
421
 * This uses the real task code for the task
422
*********************************************************************************/
423
 
424
start_rc start_task(task_p f,char tasknum)
425
  {
426
  short fp;
427
  STACK_PTR_TYPE csp;
428
  USE_CRITICAL;
429
  if (tasknum >= TASKS)
430
    return FAILED;
431
 
432
  BEGIN_CRITICAL;
433
  csp = start_stack[tasknum];
434
  stack_save[tasknum] = (STACK_PTR_TYPE)csp+16 ;
435
/* catch task return by pointing it at fallback termination function */
436
  fp =  (int)&end_run_task;
437
  *csp++     = (char)(fp & 0xff);
438
  *csp++     = (char)(fp >> 8) ;
439
/* and put a pointer to the return address which is function to call */  
440
  fp = (int)f;    
441
  *csp++     = (char)(fp &0xff);
442
  *csp++     = (char)(fp >> 8) ;
443
 
444
/* then zero out the PSW image otherwise there will be a strange crash
445
   as code tries to run in a random register bank  .... */
446
  *csp++ = 0; /* csp[4]   = acc  could pass argument here */
447
  *csp++ = 0; /* csp[5]   = b   */
448
  *csp++ = 0; /*  csp[6] = dpl    */
449
  *csp++ = 0; /*  csp[7] = dph    */
450
  *csp++ = 0; /* psw VITAL */  
451
  *csp++ = 0; /* csp [9] = r7 */
452
  *csp++ = 0; /* csp [10] = r6 */
453
  *csp++ = 0; /* csp [11] = r5 */
454
  *csp++ = 0; /* csp [12] = r4 */
455
  *csp++ = 0; /* csp [13] = r3 */
456
  *csp++ = 0; /* csp [14] = r2 */
457
  *csp++ = 0; /* csp [15] = r1 */
458
  *csp   = 0; /* csp [16] = r0 */
459
 
460
 
461
/* Set its task state as ready to run */
462
  ready |= TASK_BIT(tasknum);
463
  END_CRITICAL;
464
  return STARTED;
465
  }
466
 
467
 
468
/********************************************************************************
469
** Name :               end_run_task
470
*********************************************************************************/
471
void end_run_task(void)
472
  {
473
  USE_CRITICAL;
474
  /* whatever is running, stop it */    
475
  BEGIN_CRITICAL;
476
  ready &= ~TASK_BIT(run);
477
  task_masks[run] = 0; /* Stop any further signals making task ready to run */
478
  task_timer[run] = 0;  /* kill off pending task timeout */
479
  END_CRITICAL;
480
  reschedule();
481
/* Should never get here as this task cannot be rescheduled  */
482
  while(1);
483
 
484
  }
485
 
486
 
487
/** schedules a sleep with early return on any signals. Need to include TIMER_SIG in
488
    signal list  */
489
char wait_timed(char signal,char ticks)
490
  {
491
  USE_CRITICAL;
492
  BEGIN_CRITICAL;
493
  ready             &= ~TASK_BIT(run); /* this task is going to sleep */
494
  task_timer[run]    = ticks;
495
  task_masks[run]    = signal;   /* accept these signals as a validto wait on */
496
  END_CRITICAL;
497
  reschedule();
498
  return task_signals[run];
499
  }
500
 
501
/** A sleep of 0 means a reschedule call */
502
void sleep(char ticks)
503
  {
504
  USE_CRITICAL;
505
 
506
 
507
  if (ticks)
508
    {
509
    BEGIN_CRITICAL;
510
    ready             &= ~TASK_BIT(run); /* this task is going to sleep */
511
    task_timer[run]    = ticks;
512
    task_masks[run]    = TIMER_SIG;   /* accept this signal as a valid one to wait on */
513
    task_signals[run] &=~TIMER_SIG;
514
/* this is actually a virtual idle task here in the loop, allowing all tasks to sleep */
515
    do {
516
      END_CRITICAL;
517
      reschedule();
518
      BEGIN_CRITICAL;
519
      }
520
    while((task_signals[run] & TIMER_SIG) == 0);
521
    task_signals[run] &= ~(TIMER_SIG);
522
    task_masks[run]   &= ~TIMER_SIG;   /* accept this signal as a valid one to wait on */
523
    END_CRITICAL;
524
    }
525
  else
526
   {
527
   reschedule();
528
   }
529
 
530
  }
531
 
532
/** the current running task acknowledges the signal bits in the argument */
533
void clear_signal(char pattern)
534
  {
535
  USE_CRITICAL;
536
  BEGIN_CRITICAL;
537
  task_signals[run] &= ~(pattern);
538
  END_CRITICAL;
539
  }
540
 
541
/** Sends a signal to the task referred to. Does not actually cause rescheduling
542
  until either a T0 interrupt,  or a reschedule(), sleep() or wait_timed()
543
   call made by this task */
544
void signal(char task,char pattern)
545
  {
546
  USE_CRITICAL;
547
  BEGIN_CRITICAL;
548
/* this used in ISR context !! */
549
  INT_SIGNAL(task,pattern);
550
 
551
  END_CRITICAL;
552
  }
553
 
554
 
555
 
556
/*******************************************************************/
557
/* System initialisation call */
558
 
559
void rt_system_init(void)
560
  {
561
 
562
  AUXR  = M0 |XRS1 | XRS0  ;
563
  CKCON = WdX2 | PcaX2 | SiX2 | T2X2 | T0X2 | X2; /* T1X2 bit is '0' to double UART speed */
564
 
565
  TMOD  = 0x20; /* timer1 mode2 timer0 mode0 */
566
 
567
  EA = 0;
568
 
569
 
570
  }