comparison packages/kernel/current/src/common/clock.cxx @ 0:3111d98ba7b3 ecos-v1_1-release

Initial commit of eCos version 1.1
author jlarmour
date Tue, 11 May 1999 11:16:07 +0000
parents
children 443894e2e912
comparison
equal deleted inserted replaced
-1:000000000000 0:3111d98ba7b3
1 //==========================================================================
2 //
3 // common/clock.cxx
4 //
5 // Clock class implementations
6 //
7 //==========================================================================
8 //####COPYRIGHTBEGIN####
9 //
10 // -------------------------------------------
11 // The contents of this file are subject to the Cygnus eCos Public License
12 // Version 1.0 (the "License"); you may not use this file except in
13 // compliance with the License. You may obtain a copy of the License at
14 // http://sourceware.cygnus.com/ecos
15 //
16 // Software distributed under the License is distributed on an "AS IS"
17 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
18 // License for the specific language governing rights and limitations under
19 // the License.
20 //
21 // The Original Code is eCos - Embedded Cygnus Operating System, released
22 // September 30, 1998.
23 //
24 // The Initial Developer of the Original Code is Cygnus. Portions created
25 // by Cygnus are Copyright (C) 1998 Cygnus Solutions. All Rights Reserved.
26 // -------------------------------------------
27 //
28 //####COPYRIGHTEND####
29 //==========================================================================
30 //#####DESCRIPTIONBEGIN####
31 //
32 // Author(s): nickg
33 // Contributors: nickg
34 // Date: 1997-09-15
35 // Purpose: Clock class implementation
36 // Description: This file contains the definitions of the counter,
37 // clock and alarm class member functions that are common
38 // to all clock implementations.
39 //
40 //####DESCRIPTIONEND####
41 //
42 //==========================================================================
43
44 #include <pkgconf/kernel.h>
45
46 #include <cyg/kernel/ktypes.h> // base kernel types
47 #include <cyg/infra/cyg_trac.h> // tracing macros
48 #include <cyg/infra/cyg_ass.h> // assertion macros
49
50 #include <cyg/kernel/clock.hxx> // our header
51
52 #include <cyg/kernel/sched.hxx> // scheduler definitions
53 #include <cyg/kernel/thread.hxx> // thread definitions
54 #include <cyg/kernel/intr.hxx> // interrupt definitions
55
56 #include <cyg/kernel/sched.inl> // scheduler inlines
57 #include <cyg/kernel/clock.inl> // Clock inlines
58
59 // -------------------------------------------------------------------------
60 // Static variables
61
62 #ifdef CYGVAR_KERNEL_COUNTERS_CLOCK
63
64 Cyg_Clock *Cyg_Clock::real_time_clock = NULL; // System real time clock
65
66 #endif
67
68 //==========================================================================
69 // Constructor for counter object
70
71 Cyg_Counter::Cyg_Counter(
72 cyg_uint32 incr
73 )
74 {
75 CYG_REPORT_FUNCTION();
76
77 counter = 0;
78 increment = incr;
79 #if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST)
80
81 alarm_list = NULL; // Linear list of Alarms
82
83 #elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST)
84
85 for(cyg_ucount32 i=0; i < CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE; i++) {
86 alarm_list[i] = NULL;
87 }
88
89 #else
90 #error "No CYGIMP_KERNEL_COUNTERS_x_LIST config"
91 #endif
92
93 }
94
95 // -------------------------------------------------------------------------
96 // Destructor for Counter object
97
98 Cyg_Counter::~Cyg_Counter()
99 {
100 CYG_REPORT_FUNCTION();
101
102
103 }
104
105 // -------------------------------------------------------------------------
106 //
107
108 #ifdef CYGDBG_USE_ASSERTS
109
110 bool Cyg_Counter::check_this( cyg_assert_class_zeal zeal)
111 {
112 // check that we have a non-NULL pointer first
113 if( this == NULL ) return false;
114
115 switch( zeal )
116 {
117 case cyg_system_test:
118 case cyg_extreme:
119 case cyg_thorough:
120 case cyg_quick:
121 case cyg_trivial:
122 case cyg_none:
123 default:
124 break;
125 };
126
127 return true;
128 }
129
130 #endif
131
132 // -------------------------------------------------------------------------
133 // Counter tick function
134
135 void Cyg_Counter::tick( cyg_uint32 ticks )
136 {
137 // CYG_REPORT_FUNCTION();
138
139 CYG_ASSERTCLASS( this, "Bad counter object" );
140
141 // Increment the counter in a loop so we process
142 // each tick separately. This is easier than trying
143 // to cope with a range of increments.
144
145 while( ticks-- )
146 {
147 Cyg_Scheduler::lock();
148
149 // increment the counter, note that it is
150 // allowed to wrap.
151 counter += increment;
152
153 // now check for any expired alarms
154
155 Cyg_Alarm **alarm_list_ptr; // pointer to list
156
157 #if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST)
158
159 alarm_list_ptr = &alarm_list;
160
161 #elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST)
162
163 // With multiple lists, each one contains only the alarms
164 // that will expire at a given tick modulo the list number.
165 // So we only have a fraction of the alarms to check here.
166
167 alarm_list_ptr = &(alarm_list[
168 (counter/increment) % CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE ] );
169
170 #else
171 #error "No CYGIMP_KERNEL_COUNTERS_x_LIST config"
172 #endif
173
174 // Now that we have the list pointer, we can use common code for
175 // both list oragnizations.
176
177 while( *alarm_list_ptr != NULL )
178 {
179 Cyg_Alarm *alarm = *alarm_list_ptr;
180
181 CYG_ASSERTCLASS(alarm, "Bad alarm in counter list" );
182
183 if( alarm->trigger <= counter )
184 {
185 // remove alarm from list
186 *alarm_list_ptr = alarm->next;
187
188 if( alarm->interval != 0 )
189 {
190 // The alarm has a retrigger interval.
191 // Reset the trigger time and requeue
192 // the alarm.
193 alarm->trigger += alarm->interval;
194 add_alarm( alarm );
195 }
196 else alarm->enabled = false;
197
198 CYG_INSTRUMENT_ALARM( CALL, this, alarm );
199
200 // call alarm function
201 alarm->alarm(alarm, alarm->data);
202
203 // all done, loop
204 }
205 else break;
206 }
207
208 Cyg_Scheduler::unlock();
209
210 }
211
212 }
213
214 // -------------------------------------------------------------------------
215 // Add an alarm to this counter
216
217 void Cyg_Counter::add_alarm( Cyg_Alarm *alarm )
218 {
219 CYG_REPORT_FUNCTION();
220
221 CYG_ASSERTCLASS( this, "Bad counter object" );
222 CYG_ASSERTCLASS( alarm, "Bad alarm passed" );
223
224 Cyg_Scheduler::lock();
225
226 // set this now to allow an immediate handler call to manipulate
227 // this alarm sensibly.
228 alarm->enabled = true;
229
230 // Check here for an alarm that triggers now or in the past and
231 // call its alarm function immediately.
232 if( alarm->trigger <= counter )
233 {
234 CYG_INSTRUMENT_ALARM( CALL, this, alarm );
235
236 // call alarm function. Note that this is being
237 // called here before the add_alarm has returned.
238 // Note that this function may disable the alarm.
239
240 alarm->alarm(alarm, alarm->data);
241
242 // Note that this extra check on alarm->enabled is in case the
243 // handler function disables this alarm!
244 if( alarm->interval != 0 && alarm->enabled )
245 {
246 // The alarm has a retrigger interval.
247 // Reset the trigger interval and drop
248 // through to queue it.
249 alarm->trigger += alarm->interval;
250 // ensure the next alarm time is in our future, and in phase
251 // with the original time requested.
252 alarm->synchronize();
253 }
254 else
255 {
256 // The alarm is all done with, disable it
257 // unlock and return.
258 alarm->enabled = false;
259 Cyg_Scheduler::unlock();
260 return;
261 }
262 }
263
264 CYG_INSTRUMENT_ALARM( ADD, this, alarm );
265
266 {
267 // Find the pointer to the relevant list _after_ a retrigger
268 // alarm has been given its new trigger time.
269
270 Cyg_Alarm **alarm_list_ptr; // pointer to list
271
272 #if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST)
273
274 alarm_list_ptr = &alarm_list;
275
276 #elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST)
277
278 // Each alarm must go into the list that covers the tick that is
279 // going to happen _after_ the trigger time (or at it if trigger
280 // happens to fall on a tick.
281
282 alarm_list_ptr = &(alarm_list[
283 ((alarm->trigger+increment-1)/increment) %
284 CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE ] );
285
286 #else
287 #error "No CYGIMP_KERNEL_COUNTERS_x_LIST config"
288 #endif
289
290
291 // Now that we have the list pointer, we can use common code for
292 // both list oragnizations.
293
294 while( *alarm_list_ptr != NULL )
295 {
296 Cyg_Alarm *list_alarm = *alarm_list_ptr;
297
298 CYG_ASSERTCLASS(list_alarm, "Bad alarm in counter list" );
299
300 // The alarms are in ascending trigger order. When we
301 // find an alarm that is later than us, we go in front of
302 // it.
303
304 if( list_alarm->trigger > alarm->trigger ) break;
305 else alarm_list_ptr = &list_alarm->next;
306 }
307
308 // Insert the new alarm at *alarm_list_ptr
309
310 alarm->next = *alarm_list_ptr;
311 *alarm_list_ptr = alarm;
312
313 Cyg_Scheduler::unlock();
314 }
315 }
316
317 // -------------------------------------------------------------------------
318 // Remove an alarm from this counter
319
320 void Cyg_Counter::rem_alarm( Cyg_Alarm *alarm )
321 {
322 CYG_REPORT_FUNCTION();
323
324 CYG_ASSERTCLASS( this, "Bad counter object" );
325 CYG_ASSERTCLASS( alarm, "Bad alarm passed" );
326
327 Cyg_Alarm **alarm_list_ptr; // pointer to list
328
329 #if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST)
330
331 alarm_list_ptr = &alarm_list;
332
333 #elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST)
334
335 alarm_list_ptr = &(alarm_list[
336 ((alarm->trigger+increment-1)/increment) %
337 CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE ] );
338
339 #else
340 #error "No CYGIMP_KERNEL_COUNTERS_x_LIST config"
341 #endif
342
343 // Now that we have the list pointer, we can use common code for
344 // both list organizations.
345
346 Cyg_Scheduler::lock();
347
348 CYG_INSTRUMENT_ALARM( REM, this, alarm );
349
350 while( *alarm_list_ptr != NULL )
351 {
352 Cyg_Alarm *list_alarm = *alarm_list_ptr;
353
354 CYG_ASSERTCLASS(list_alarm, "Bad alarm in counter list" );
355
356 if( list_alarm == alarm ) break;
357 else alarm_list_ptr = &list_alarm->next;
358 }
359
360 // If the alarm was found, remove it from the list.
361 if( *alarm_list_ptr != NULL )
362 {
363 *alarm_list_ptr = alarm->next;
364 alarm->enabled = false;
365 }
366
367 Cyg_Scheduler::unlock();
368 }
369
370 //==========================================================================
371 // Constructor for clock object
372
373 Cyg_Clock::Cyg_Clock(
374 cyg_resolution res
375 )
376 {
377 CYG_REPORT_FUNCTION();
378
379 resolution = res;
380 }
381
382 // -------------------------------------------------------------------------
383 // Destructor for Clock objects
384
385 Cyg_Clock::~Cyg_Clock()
386 {
387 CYG_REPORT_FUNCTION();
388
389 }
390
391 // -------------------------------------------------------------------------
392 //
393
394 #ifdef CYGDBG_USE_ASSERTS
395
396 bool Cyg_Clock::check_this( cyg_assert_class_zeal zeal)
397 {
398 // check that we have a non-NULL pointer first
399 if( this == NULL ) return false;
400
401 switch( zeal )
402 {
403 case cyg_system_test:
404 case cyg_extreme:
405 case cyg_thorough:
406 case cyg_quick:
407 case cyg_trivial:
408 case cyg_none:
409 default:
410 break;
411 };
412
413 return true;
414 }
415
416 #endif
417
418 //==========================================================================
419 // Constructor for alarm object
420
421 Cyg_Alarm::Cyg_Alarm(
422 Cyg_Counter *c, // Attached to this counter
423 cyg_alarm_fn *a, // Call-back function
424 CYG_ADDRWORD d // Call-back data
425 )
426 {
427 CYG_REPORT_FUNCTION();
428
429 counter = c;
430 alarm = a;
431 data = d;
432 trigger = 0;
433 interval = 0;
434 enabled = false;
435
436 #if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST) || defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST)
437 next = NULL;
438 #endif
439
440 }
441
442 Cyg_Alarm::Cyg_Alarm(){}
443
444 // -------------------------------------------------------------------------
445 // Destructor
446
447 Cyg_Alarm::~Cyg_Alarm()
448 {
449 CYG_REPORT_FUNCTION();
450
451 disable();
452 }
453
454 // -------------------------------------------------------------------------
455 //
456
457 #ifdef CYGDBG_USE_ASSERTS
458
459 bool Cyg_Alarm::check_this( cyg_assert_class_zeal zeal)
460 {
461 // check that we have a non-NULL pointer first
462 if( this == NULL ) return false;
463
464 switch( zeal )
465 {
466 case cyg_system_test:
467 case cyg_extreme:
468 case cyg_thorough:
469 if( trigger != 0 && !enabled ) return false;
470 case cyg_quick:
471 case cyg_trivial:
472 case cyg_none:
473 default:
474 break;
475 };
476
477 return true;
478 }
479
480 #endif
481
482 // -------------------------------------------------------------------------
483 // Initialize Alarm and enable
484
485 void Cyg_Alarm::initialize(
486 cyg_tick_count t, // Absolute trigger time
487 cyg_tick_count i // Relative retrigger interval
488 )
489 {
490 CYG_REPORT_FUNCTION();
491
492 // If already enabled, remove from counter
493
494 if( enabled ) counter->rem_alarm(this);
495
496 CYG_INSTRUMENT_ALARM( INIT, this, 0 );
497 CYG_INSTRUMENT_ALARM( TRIGGER,
498 ((cyg_uint32 *)&t)[0],
499 ((cyg_uint32 *)&t)[1] );
500 CYG_INSTRUMENT_ALARM( INTERVAL,
501 ((cyg_uint32 *)&i)[0],
502 ((cyg_uint32 *)&i)[1] );
503
504 trigger = t;
505 interval = i;
506
507 counter->add_alarm(this);
508 }
509
510 // -------------------------------------------------------------------------
511 // Synchronize with a past alarm stream that had been disabled,
512 // bring past times into synch, and the like.
513
514 void
515 Cyg_Alarm::synchronize( void )
516 {
517 if( interval != 0 ) {
518 // This expression sets the trigger to the next whole interval
519 // at or after the current time. This means that alarms will
520 // continue at the same intervals as if they had never been
521 // disabled. The alternative would be to just set trigger to
522 // (counter->counter + interval), but this is less satisfying
523 // than preserving the original intervals. That behaviour can
524 // always be obtained by using initialize() rather than
525 // enable(), while the current behaviour would be more
526 // difficult to achieve that way.
527 cyg_tick_count d;
528 d = counter->current_value() + interval - trigger;
529 if ( d > interval ) {
530 // then trigger was in the past, so resynchronize
531 trigger += interval * ((d - 1) / interval );
532 }
533 // otherwise, we were just set up, so no worries.
534 }
535 }
536
537 // -------------------------------------------------------------------------
538 // Ensure alarm enabled
539
540 void Cyg_Alarm::enable()
541 {
542 if( !enabled )
543 {
544 // ensure the alarm time is in our future:
545 synchronize();
546 enabled = true;
547 counter->add_alarm(this);
548 }
549 }
550
551 //==========================================================================
552 // System clock object
553
554 #ifdef CYGVAR_KERNEL_COUNTERS_CLOCK
555
556 class Cyg_RealTimeClock
557 : public Cyg_Clock
558 {
559 Cyg_Interrupt interrupt;
560
561 static cyg_uint32 isr(cyg_vector vector, CYG_ADDRWORD data);
562
563 static void dsr(cyg_vector vector, cyg_ucount32 count, CYG_ADDRWORD data);
564
565 Cyg_RealTimeClock();
566
567 static Cyg_RealTimeClock rtc;
568 };
569
570 Cyg_Clock::cyg_resolution rtc_resolution = CYGNUM_KERNEL_COUNTERS_RTC_RESOLUTION;
571
572 //Cyg_RealTimeClock Cyg_RealTimeClock::rtc __attribute__((init_priority (1)));
573
574 Cyg_RealTimeClock Cyg_RealTimeClock::rtc CYG_INIT_PRIORITY( CLOCK );
575
576 // -------------------------------------------------------------------------
577
578 Cyg_RealTimeClock::Cyg_RealTimeClock()
579 : Cyg_Clock(rtc_resolution),
580 interrupt(CYG_VECTOR_RTC, 1, (CYG_ADDRWORD)this, isr, dsr)
581 {
582 CYG_REPORT_FUNCTION();
583
584 HAL_CLOCK_INITIALIZE( CYGNUM_KERNEL_COUNTERS_RTC_PERIOD );
585
586 interrupt.attach();
587
588 interrupt.unmask_interrupt(CYG_VECTOR_RTC);
589
590 Cyg_Clock::real_time_clock = this;
591 }
592
593 // -------------------------------------------------------------------------
594
595 cyg_uint32 Cyg_RealTimeClock::isr(cyg_vector vector, CYG_ADDRWORD data)
596 {
597 // CYG_REPORT_FUNCTION();
598
599 CYG_INSTRUMENT_CLOCK( ISR, 0, 0);
600
601 HAL_CLOCK_RESET( CYG_VECTOR_RTC, CYGNUM_KERNEL_COUNTERS_RTC_PERIOD );
602
603 Cyg_Interrupt::acknowledge_interrupt(CYG_VECTOR_RTC);
604
605 return Cyg_Interrupt::CALL_DSR;
606 }
607
608 // -------------------------------------------------------------------------
609
610 void Cyg_RealTimeClock::dsr(cyg_vector vector, cyg_ucount32 count, CYG_ADDRWORD data)
611 {
612 // CYG_REPORT_FUNCTION();
613
614 Cyg_RealTimeClock *rtc = (Cyg_RealTimeClock *)data;
615
616 CYG_INSTRUMENT_CLOCK( TICK_START,
617 rtc->current_value_lo(),
618 rtc->current_value_hi());
619
620 rtc->tick( count );
621
622 #ifdef CYGSEM_KERNEL_SCHED_TIMESLICE
623 #if 0 == CYG_SCHED_UNIQUE_PRIORITIES
624
625 // If timeslicing is enabled, call the scheduler to
626 // handle it. But not if we have unique priorities.
627
628 Cyg_Scheduler::scheduler.timeslice();
629
630 #endif
631 #endif
632
633 CYG_INSTRUMENT_CLOCK( TICK_END,
634 rtc->current_value_lo(),
635 rtc->current_value_hi());
636
637 }
638
639 #endif
640
641 // -------------------------------------------------------------------------
642 // EOF common/clock.cxx