Mercurial > flash_v2
view 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 |
line wrap: on
line source
//========================================================================== // // common/clock.cxx // // Clock class implementations // //========================================================================== //####COPYRIGHTBEGIN#### // // ------------------------------------------- // The contents of this file are subject to the Cygnus eCos Public License // Version 1.0 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://sourceware.cygnus.com/ecos // // Software distributed under the License is distributed on an "AS IS" // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the // License for the specific language governing rights and limitations under // the License. // // The Original Code is eCos - Embedded Cygnus Operating System, released // September 30, 1998. // // The Initial Developer of the Original Code is Cygnus. Portions created // by Cygnus are Copyright (C) 1998 Cygnus Solutions. All Rights Reserved. // ------------------------------------------- // //####COPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): nickg // Contributors: nickg // Date: 1997-09-15 // Purpose: Clock class implementation // Description: This file contains the definitions of the counter, // clock and alarm class member functions that are common // to all clock implementations. // //####DESCRIPTIONEND#### // //========================================================================== #include <pkgconf/kernel.h> #include <cyg/kernel/ktypes.h> // base kernel types #include <cyg/infra/cyg_trac.h> // tracing macros #include <cyg/infra/cyg_ass.h> // assertion macros #include <cyg/kernel/clock.hxx> // our header #include <cyg/kernel/sched.hxx> // scheduler definitions #include <cyg/kernel/thread.hxx> // thread definitions #include <cyg/kernel/intr.hxx> // interrupt definitions #include <cyg/kernel/sched.inl> // scheduler inlines #include <cyg/kernel/clock.inl> // Clock inlines // ------------------------------------------------------------------------- // Static variables #ifdef CYGVAR_KERNEL_COUNTERS_CLOCK Cyg_Clock *Cyg_Clock::real_time_clock = NULL; // System real time clock #endif //========================================================================== // Constructor for counter object Cyg_Counter::Cyg_Counter( cyg_uint32 incr ) { CYG_REPORT_FUNCTION(); counter = 0; increment = incr; #if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST) alarm_list = NULL; // Linear list of Alarms #elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST) for(cyg_ucount32 i=0; i < CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE; i++) { alarm_list[i] = NULL; } #else #error "No CYGIMP_KERNEL_COUNTERS_x_LIST config" #endif } // ------------------------------------------------------------------------- // Destructor for Counter object Cyg_Counter::~Cyg_Counter() { CYG_REPORT_FUNCTION(); } // ------------------------------------------------------------------------- // #ifdef CYGDBG_USE_ASSERTS bool Cyg_Counter::check_this( cyg_assert_class_zeal zeal) { // check that we have a non-NULL pointer first if( this == NULL ) return false; switch( zeal ) { case cyg_system_test: case cyg_extreme: case cyg_thorough: case cyg_quick: case cyg_trivial: case cyg_none: default: break; }; return true; } #endif // ------------------------------------------------------------------------- // Counter tick function void Cyg_Counter::tick( cyg_uint32 ticks ) { // CYG_REPORT_FUNCTION(); CYG_ASSERTCLASS( this, "Bad counter object" ); // Increment the counter in a loop so we process // each tick separately. This is easier than trying // to cope with a range of increments. while( ticks-- ) { Cyg_Scheduler::lock(); // increment the counter, note that it is // allowed to wrap. counter += increment; // now check for any expired alarms Cyg_Alarm **alarm_list_ptr; // pointer to list #if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST) alarm_list_ptr = &alarm_list; #elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST) // With multiple lists, each one contains only the alarms // that will expire at a given tick modulo the list number. // So we only have a fraction of the alarms to check here. alarm_list_ptr = &(alarm_list[ (counter/increment) % CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE ] ); #else #error "No CYGIMP_KERNEL_COUNTERS_x_LIST config" #endif // Now that we have the list pointer, we can use common code for // both list oragnizations. while( *alarm_list_ptr != NULL ) { Cyg_Alarm *alarm = *alarm_list_ptr; CYG_ASSERTCLASS(alarm, "Bad alarm in counter list" ); if( alarm->trigger <= counter ) { // remove alarm from list *alarm_list_ptr = alarm->next; if( alarm->interval != 0 ) { // The alarm has a retrigger interval. // Reset the trigger time and requeue // the alarm. alarm->trigger += alarm->interval; add_alarm( alarm ); } else alarm->enabled = false; CYG_INSTRUMENT_ALARM( CALL, this, alarm ); // call alarm function alarm->alarm(alarm, alarm->data); // all done, loop } else break; } Cyg_Scheduler::unlock(); } } // ------------------------------------------------------------------------- // Add an alarm to this counter void Cyg_Counter::add_alarm( Cyg_Alarm *alarm ) { CYG_REPORT_FUNCTION(); CYG_ASSERTCLASS( this, "Bad counter object" ); CYG_ASSERTCLASS( alarm, "Bad alarm passed" ); Cyg_Scheduler::lock(); // set this now to allow an immediate handler call to manipulate // this alarm sensibly. alarm->enabled = true; // Check here for an alarm that triggers now or in the past and // call its alarm function immediately. if( alarm->trigger <= counter ) { CYG_INSTRUMENT_ALARM( CALL, this, alarm ); // call alarm function. Note that this is being // called here before the add_alarm has returned. // Note that this function may disable the alarm. alarm->alarm(alarm, alarm->data); // Note that this extra check on alarm->enabled is in case the // handler function disables this alarm! if( alarm->interval != 0 && alarm->enabled ) { // The alarm has a retrigger interval. // Reset the trigger interval and drop // through to queue it. alarm->trigger += alarm->interval; // ensure the next alarm time is in our future, and in phase // with the original time requested. alarm->synchronize(); } else { // The alarm is all done with, disable it // unlock and return. alarm->enabled = false; Cyg_Scheduler::unlock(); return; } } CYG_INSTRUMENT_ALARM( ADD, this, alarm ); { // Find the pointer to the relevant list _after_ a retrigger // alarm has been given its new trigger time. Cyg_Alarm **alarm_list_ptr; // pointer to list #if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST) alarm_list_ptr = &alarm_list; #elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST) // Each alarm must go into the list that covers the tick that is // going to happen _after_ the trigger time (or at it if trigger // happens to fall on a tick. alarm_list_ptr = &(alarm_list[ ((alarm->trigger+increment-1)/increment) % CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE ] ); #else #error "No CYGIMP_KERNEL_COUNTERS_x_LIST config" #endif // Now that we have the list pointer, we can use common code for // both list oragnizations. while( *alarm_list_ptr != NULL ) { Cyg_Alarm *list_alarm = *alarm_list_ptr; CYG_ASSERTCLASS(list_alarm, "Bad alarm in counter list" ); // The alarms are in ascending trigger order. When we // find an alarm that is later than us, we go in front of // it. if( list_alarm->trigger > alarm->trigger ) break; else alarm_list_ptr = &list_alarm->next; } // Insert the new alarm at *alarm_list_ptr alarm->next = *alarm_list_ptr; *alarm_list_ptr = alarm; Cyg_Scheduler::unlock(); } } // ------------------------------------------------------------------------- // Remove an alarm from this counter void Cyg_Counter::rem_alarm( Cyg_Alarm *alarm ) { CYG_REPORT_FUNCTION(); CYG_ASSERTCLASS( this, "Bad counter object" ); CYG_ASSERTCLASS( alarm, "Bad alarm passed" ); Cyg_Alarm **alarm_list_ptr; // pointer to list #if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST) alarm_list_ptr = &alarm_list; #elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST) alarm_list_ptr = &(alarm_list[ ((alarm->trigger+increment-1)/increment) % CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE ] ); #else #error "No CYGIMP_KERNEL_COUNTERS_x_LIST config" #endif // Now that we have the list pointer, we can use common code for // both list organizations. Cyg_Scheduler::lock(); CYG_INSTRUMENT_ALARM( REM, this, alarm ); while( *alarm_list_ptr != NULL ) { Cyg_Alarm *list_alarm = *alarm_list_ptr; CYG_ASSERTCLASS(list_alarm, "Bad alarm in counter list" ); if( list_alarm == alarm ) break; else alarm_list_ptr = &list_alarm->next; } // If the alarm was found, remove it from the list. if( *alarm_list_ptr != NULL ) { *alarm_list_ptr = alarm->next; alarm->enabled = false; } Cyg_Scheduler::unlock(); } //========================================================================== // Constructor for clock object Cyg_Clock::Cyg_Clock( cyg_resolution res ) { CYG_REPORT_FUNCTION(); resolution = res; } // ------------------------------------------------------------------------- // Destructor for Clock objects Cyg_Clock::~Cyg_Clock() { CYG_REPORT_FUNCTION(); } // ------------------------------------------------------------------------- // #ifdef CYGDBG_USE_ASSERTS bool Cyg_Clock::check_this( cyg_assert_class_zeal zeal) { // check that we have a non-NULL pointer first if( this == NULL ) return false; switch( zeal ) { case cyg_system_test: case cyg_extreme: case cyg_thorough: case cyg_quick: case cyg_trivial: case cyg_none: default: break; }; return true; } #endif //========================================================================== // Constructor for alarm object Cyg_Alarm::Cyg_Alarm( Cyg_Counter *c, // Attached to this counter cyg_alarm_fn *a, // Call-back function CYG_ADDRWORD d // Call-back data ) { CYG_REPORT_FUNCTION(); counter = c; alarm = a; data = d; trigger = 0; interval = 0; enabled = false; #if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST) || defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST) next = NULL; #endif } Cyg_Alarm::Cyg_Alarm(){} // ------------------------------------------------------------------------- // Destructor Cyg_Alarm::~Cyg_Alarm() { CYG_REPORT_FUNCTION(); disable(); } // ------------------------------------------------------------------------- // #ifdef CYGDBG_USE_ASSERTS bool Cyg_Alarm::check_this( cyg_assert_class_zeal zeal) { // check that we have a non-NULL pointer first if( this == NULL ) return false; switch( zeal ) { case cyg_system_test: case cyg_extreme: case cyg_thorough: if( trigger != 0 && !enabled ) return false; case cyg_quick: case cyg_trivial: case cyg_none: default: break; }; return true; } #endif // ------------------------------------------------------------------------- // Initialize Alarm and enable void Cyg_Alarm::initialize( cyg_tick_count t, // Absolute trigger time cyg_tick_count i // Relative retrigger interval ) { CYG_REPORT_FUNCTION(); // If already enabled, remove from counter if( enabled ) counter->rem_alarm(this); CYG_INSTRUMENT_ALARM( INIT, this, 0 ); CYG_INSTRUMENT_ALARM( TRIGGER, ((cyg_uint32 *)&t)[0], ((cyg_uint32 *)&t)[1] ); CYG_INSTRUMENT_ALARM( INTERVAL, ((cyg_uint32 *)&i)[0], ((cyg_uint32 *)&i)[1] ); trigger = t; interval = i; counter->add_alarm(this); } // ------------------------------------------------------------------------- // Synchronize with a past alarm stream that had been disabled, // bring past times into synch, and the like. void Cyg_Alarm::synchronize( void ) { if( interval != 0 ) { // This expression sets the trigger to the next whole interval // at or after the current time. This means that alarms will // continue at the same intervals as if they had never been // disabled. The alternative would be to just set trigger to // (counter->counter + interval), but this is less satisfying // than preserving the original intervals. That behaviour can // always be obtained by using initialize() rather than // enable(), while the current behaviour would be more // difficult to achieve that way. cyg_tick_count d; d = counter->current_value() + interval - trigger; if ( d > interval ) { // then trigger was in the past, so resynchronize trigger += interval * ((d - 1) / interval ); } // otherwise, we were just set up, so no worries. } } // ------------------------------------------------------------------------- // Ensure alarm enabled void Cyg_Alarm::enable() { if( !enabled ) { // ensure the alarm time is in our future: synchronize(); enabled = true; counter->add_alarm(this); } } //========================================================================== // System clock object #ifdef CYGVAR_KERNEL_COUNTERS_CLOCK class Cyg_RealTimeClock : public Cyg_Clock { Cyg_Interrupt interrupt; static cyg_uint32 isr(cyg_vector vector, CYG_ADDRWORD data); static void dsr(cyg_vector vector, cyg_ucount32 count, CYG_ADDRWORD data); Cyg_RealTimeClock(); static Cyg_RealTimeClock rtc; }; Cyg_Clock::cyg_resolution rtc_resolution = CYGNUM_KERNEL_COUNTERS_RTC_RESOLUTION; //Cyg_RealTimeClock Cyg_RealTimeClock::rtc __attribute__((init_priority (1))); Cyg_RealTimeClock Cyg_RealTimeClock::rtc CYG_INIT_PRIORITY( CLOCK ); // ------------------------------------------------------------------------- Cyg_RealTimeClock::Cyg_RealTimeClock() : Cyg_Clock(rtc_resolution), interrupt(CYG_VECTOR_RTC, 1, (CYG_ADDRWORD)this, isr, dsr) { CYG_REPORT_FUNCTION(); HAL_CLOCK_INITIALIZE( CYGNUM_KERNEL_COUNTERS_RTC_PERIOD ); interrupt.attach(); interrupt.unmask_interrupt(CYG_VECTOR_RTC); Cyg_Clock::real_time_clock = this; } // ------------------------------------------------------------------------- cyg_uint32 Cyg_RealTimeClock::isr(cyg_vector vector, CYG_ADDRWORD data) { // CYG_REPORT_FUNCTION(); CYG_INSTRUMENT_CLOCK( ISR, 0, 0); HAL_CLOCK_RESET( CYG_VECTOR_RTC, CYGNUM_KERNEL_COUNTERS_RTC_PERIOD ); Cyg_Interrupt::acknowledge_interrupt(CYG_VECTOR_RTC); return Cyg_Interrupt::CALL_DSR; } // ------------------------------------------------------------------------- void Cyg_RealTimeClock::dsr(cyg_vector vector, cyg_ucount32 count, CYG_ADDRWORD data) { // CYG_REPORT_FUNCTION(); Cyg_RealTimeClock *rtc = (Cyg_RealTimeClock *)data; CYG_INSTRUMENT_CLOCK( TICK_START, rtc->current_value_lo(), rtc->current_value_hi()); rtc->tick( count ); #ifdef CYGSEM_KERNEL_SCHED_TIMESLICE #if 0 == CYG_SCHED_UNIQUE_PRIORITIES // If timeslicing is enabled, call the scheduler to // handle it. But not if we have unique priorities. Cyg_Scheduler::scheduler.timeslice(); #endif #endif CYG_INSTRUMENT_CLOCK( TICK_END, rtc->current_value_lo(), rtc->current_value_hi()); } #endif // ------------------------------------------------------------------------- // EOF common/clock.cxx
