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