Mercurial > flash_v2
diff 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 diff
new file mode 100644 --- /dev/null +++ b/packages/kernel/current/src/common/clock.cxx @@ -0,0 +1,642 @@ +//========================================================================== +// +// 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
