OpenCores
URL https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk

Subversion Repositories openrisc_me

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [packages/] [kernel/] [v2_0/] [src/] [common/] [clock.cxx] - Rev 446

Go to most recent revision | Compare with Previous | Blame | View Log

//==========================================================================
//
//      common/clock.cxx
//
//      Clock class implementations
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Jonathan Larmour
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####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;
 
}
 
// -------------------------------------------------------------------------
// Destructor for Counter object
 
Cyg_Counter::~Cyg_Counter()
{
    CYG_REPORT_FUNCTION();
 
 
}
 
// -------------------------------------------------------------------------
// 
 
#ifdef CYGDBG_USE_ASSERTS
 
cyg_bool Cyg_Counter::check_this( cyg_assert_class_zeal zeal) const
{
    // 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_List *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.
 
#ifdef CYGIMP_KERNEL_COUNTERS_SORT_LIST
 
        // With a sorted alarm list, we can simply pick alarms off the
        // front of the list until we find one that is in the future.
 
        while( !alarm_list_ptr->empty() )
        {
            Cyg_Alarm *alarm = alarm_list_ptr->get_head();
 
            CYG_ASSERTCLASS(alarm, "Bad alarm in counter list" );
 
            if( alarm->trigger <= counter )
            {
                // remove alarm from list
                alarm_list_ptr->rem_head();
 
                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;
 
        } 
#else
 
        // With an unsorted list, we must scan the whole list for
        // candidates. We move the whole list to a temporary location
        // before doing this so that we are not disturbed by new
        // alarms being added to the list. As we consider and
        // eliminate alarms we put them onto the done_list and at the
        // end we then move it back to where it belongs.
 
        Cyg_Alarm_List done_list;
 
        Cyg_Alarm_List alarm_list;
 
        alarm_list.merge( *alarm_list_ptr );
 
        while( !alarm_list.empty() )
        {
            Cyg_Alarm *alarm = alarm_list.rem_head();
 
            CYG_ASSERTCLASS(alarm, "Bad alarm in counter list" );
 
            if( alarm->trigger <= counter )
            {
                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
            {
                // add unused alarm to done list.
                done_list.add_tail(alarm);
            }
        }
 
        // Return done list to real list. If any alarms have been
        // added to the alarm list while we have been scanning then
        // the done list will be added behind them.
 
        alarm_list_ptr->merge( done_list );
 
#endif        
        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_List *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
 
#ifdef CYGIMP_KERNEL_COUNTERS_SORT_LIST
 
    // Now that we have the list pointer, we can use common code for
    // both list organizations.
 
    Cyg_Alarm *list_alarm = alarm_list_ptr->get_head();
 
    if( list_alarm != NULL )
    {
        do
        {
            CYG_ASSERTCLASS(list_alarm, "Bad alarm in counter list" );
 
            // The alarms are in ascending trigger order. If we
            // find an alarm that triggers later than us, we go
            // in front of it.
 
            if( list_alarm->trigger > alarm->trigger )
            {
                alarm_list_ptr->insert( list_alarm, alarm );
                goto add_alarm_unlock_return;
            }
 
            list_alarm = list_alarm->get_next();
 
        } while( list_alarm != alarm_list_ptr->get_head() );
        // a lower or equal alarm time was not found, so drop through
        // so it is added to the list tail
    }
    alarm_list_ptr->add_tail( alarm );
 
 add_alarm_unlock_return:
#else    
 
    alarm_list_ptr->add_tail( alarm );
 
#endif
 
    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_List *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 );
 
    alarm_list_ptr->remove( alarm );
 
    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
 
cyg_bool Cyg_Clock::check_this( cyg_assert_class_zeal zeal) const
{
    // 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
 
// -------------------------------------------------------------------------
// 
// Clock Converters: split a rational into 4 factors to try to prevent
// overflow whilst retaining reasonable accuracy.
// 
// typically we get numbers like 1,000,000 for ns_per and
// 100 and 1,000,000,000 for the dividend and divisor.
// So we want answers like 1/10 and 10/1 out of these routines.
 
static void construct_converter( Cyg_Clock::converter *pcc,
                                        cyg_uint64 m1, cyg_uint64 d1,
                                        cyg_uint64 m2, cyg_uint64 d2 )
{
    cyg_uint64 upper, lower;
    unsigned int i;
    static cyg_uint16 primes[] = {
        3,5,7,11,13,17,19,23,29,31,37,41,43,47,
        53,59,61,67,71,73,79,83,89,97,
        101,103,107,109,113,127,131,137,139,149,
        151,157,163,167,173,179,181,191,193,197,199,
        239,                            // for 1,111,111
        541,                            // for 10,101,011
        1667,                           // for 8,333,333
    };
 
    int rounding = 0;
 
    // Here we assume that our workings will fit in a 64; the point is to
    // allow calculations with a number of ticks that may be large.
    upper = m1 * m2;
    lower = d1 * d2;
#ifdef CYGDBG_USE_ASSERTS
    cyg_uint64 save_upper = upper;
    cyg_uint64 save_lower = lower;
#endif
 
 retry_rounding:
    // First strip out common powers of 2
    while ( (0 == (1 & upper)) && ( 0 == (1 & lower)) ) {
        upper >>= 1;
        lower >>= 1;
    }
 
    // then common factors - use lazy table above
    for ( i = 0 ; i < (sizeof( primes )/sizeof( primes[0] )); i++ ) {
        cyg_uint64 j, k, p = (cyg_uint64)(primes[i]);
        j = upper / p;
        while ( j * p == upper ) {
            k = lower / p;
            if ( k * p != lower )
                break;
            upper = j;
            lower = k;
            j = upper / p;
        }
    }
 
    m1 = upper;
    d1 = lower;
    m2 = 1;
    d2 = 1;
 
    if ( m1 > 0x10000 ) {
        // only bother if there are more than 16 bits consumed here
 
        // now move powers of 2 from d1 to d2
        // keeping them the same order of magnitude
        while ( (0 == (1 & d1)) && (d2 < d1) ) {
            d1 >>= 1;
            d2 <<= 1;
        }
 
        // and factors from the table - go too far, if anything
        int cont = (d2 < d1);
        for ( i = 0 ; cont && (i < (sizeof( primes )/sizeof( primes[0] ))); i++ ) {
            cyg_uint64 k, p = (cyg_uint64)(primes[i]);
            k = d1 / p;
            while ( cont && ((k * p) == d1) ) {
                // we can extract a prime
                d1 = k;
                d2 *= p;
                k = d1 / p;
                cont = (d2 < d1);
            }
        }
 
        // move powers of 2 from m1 to m2 so long as we do not go less than d1
        while ( (0 == (1 & m1)) && (m2 < m1) && (m1 > (d1 << 5)) ) {
            m1 >>= 1;
            m2 <<= 1;
            if ( m1 < 0x10000 )
                break;
        }
 
        // and factors from the table - ensure m1 stays well larger than d1
        cont = ((m2 < m1) && (m1 > (d1 << 4)) && (m1 > 0x10000));
        for ( i = 0 ; cont && (i < (sizeof( primes )/sizeof( primes[0] ))); i++ ) {
            cyg_uint64 k, p = (cyg_uint64)(primes[i]);
            k = m1 / p;
            cont = cont && (k > (d1 << 4) && (k > 0x10000));
            while ( cont && ((k * p) == m1) ) {
                // we can extract a prime
                m1 = k;
                m2 *= p;
                k = m1 / p; // examine k for getting too small
                cont = ((m2 < m1) && (k > (d1 << 4)) && (k > 0x10000));
            }
        }
 
        // if, after all that, m1 odd and unchanged, and too large,
        // decrement it just the once and try again: then try it
        // incremented once.
        if ( (m1 & 1) && (m1 == upper) && (m1 > 0x10000) && (rounding < 2) ) {
            CYG_ASSERT( 1 == m2, "m2 should be 1 to try rounding" );
            m1--;
            upper = m1;
            rounding++;
            goto retry_rounding;
        }
        // likewise for d1 - each of the pair can be odd only once each
        if ( (d1 & 1) && (d1 == lower) && (d1 > 0x10000) && (rounding < 2) ) {
            CYG_ASSERT( 1 == d2, "d2 should be 1 to try rounding" );
            d1--;
            lower = d1;
            rounding++;
            goto retry_rounding;
        }
    }
 
    CYG_ASSERT( 0 != m1, "m1 zero" );
    CYG_ASSERT( 0 != m2, "m2 zero" );
    CYG_ASSERT( 0 != d1, "d1 zero" );
    CYG_ASSERT( 0 != d2, "d2 zero" );
    CYG_ASSERT( rounding || save_upper/save_lower == (m1 * m2)/(d1 * d2),
                "Unequal in forwards direction" );
    CYG_ASSERT( rounding || save_lower/save_upper == (d1 * d2)/(m1 * m2),
                "Unequal in reverse direction" );
 
    pcc->mul1 = m1;
    pcc->div1 = d1;
    pcc->mul2 = m2;
    pcc->div2 = d2;
}
 
// other to clocks is (other * ns_per * dividend / divisor)
void Cyg_Clock::get_other_to_clock_converter(
    cyg_uint64 ns_per_other_tick,
    struct converter *pcc )
{
    construct_converter( pcc,
                         ns_per_other_tick, 1,
                         resolution.divisor, resolution.dividend );
}
 
// clocks to other is (ticks * divisor / dividend / ns_per)
void Cyg_Clock::get_clock_to_other_converter(
    cyg_uint64 ns_per_other_tick,
    struct converter *pcc )
{
    construct_converter( pcc,
                         1, ns_per_other_tick,
                         resolution.dividend, resolution.divisor );
}
 
 
//==========================================================================
// 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;
 
}
 
Cyg_Alarm::Cyg_Alarm(){}
 
// -------------------------------------------------------------------------
// Destructor
 
Cyg_Alarm::~Cyg_Alarm()
{
    CYG_REPORT_FUNCTION();
 
    disable();
}
 
// -------------------------------------------------------------------------
// 
 
#ifdef CYGDBG_USE_ASSERTS
 
cyg_bool Cyg_Alarm::check_this( cyg_assert_class_zeal zeal) const
{
    // 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);
    }
}
 
// -------------------------------------------------------------------------
// Get the current time values from the alarm
 
void Cyg_Alarm::get_times(
        cyg_tick_count  *t,      // Next trigger time
        cyg_tick_count  *i       // Current interval
        )
{
    // Lock the scheduler while we do this to avoid
    // race conditions.
    Cyg_Scheduler::lock();
 
    if( t != NULL ) *t = trigger;
    if( i != NULL ) *i = interval;
 
    Cyg_Scheduler::unlock();
}
 
//==========================================================================
// 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(CYGNUM_HAL_INTERRUPT_RTC, 1, (CYG_ADDRWORD)this, isr, dsr)
{
    CYG_REPORT_FUNCTION();
 
    HAL_CLOCK_INITIALIZE( CYGNUM_KERNEL_COUNTERS_RTC_PERIOD );
 
    interrupt.attach();
 
    interrupt.unmask_interrupt(CYGNUM_HAL_INTERRUPT_RTC);
 
    Cyg_Clock::real_time_clock = this;
}
 
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_LATENCY) && defined(HAL_CLOCK_LATENCY)
cyg_tick_count total_clock_latency, total_clock_interrupts;
cyg_int32 min_clock_latency = 0x7FFFFFFF;
cyg_int32 max_clock_latency = 0;
bool measure_clock_latency = false;
#endif
 
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_DSR_LATENCY)
cyg_tick_count total_clock_dsr_latency, total_clock_dsr_calls;
cyg_int32 min_clock_dsr_latency = 0x7FFFFFFF;
cyg_int32 max_clock_dsr_latency = 0;
cyg_uint32 clock_dsr_start = 0;
#endif
 
// -------------------------------------------------------------------------
 
cyg_uint32 Cyg_RealTimeClock::isr(cyg_vector vector, CYG_ADDRWORD data)
{
//    CYG_REPORT_FUNCTION();
 
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_LATENCY) && defined(HAL_CLOCK_LATENCY)
    if (measure_clock_latency) {
        cyg_int32 delta;
        HAL_CLOCK_LATENCY(&delta);
        // Note: Ignore a latency of 0 when finding min_clock_latency.
        if (delta > 0) {
            // Valid delta measured
            total_clock_latency += delta;
            total_clock_interrupts++;
            if (min_clock_latency > delta) min_clock_latency = delta;
            if (max_clock_latency < delta) max_clock_latency = delta;
        }
    }
#endif
 
    CYG_INSTRUMENT_CLOCK( ISR, 0, 0);
 
    HAL_CLOCK_RESET( CYGNUM_HAL_INTERRUPT_RTC, CYGNUM_KERNEL_COUNTERS_RTC_PERIOD );
 
    Cyg_Interrupt::acknowledge_interrupt(CYGNUM_HAL_INTERRUPT_RTC);
 
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_DSR_LATENCY)
    HAL_CLOCK_READ(&clock_dsr_start);
#endif    
    return Cyg_Interrupt::CALL_DSR|Cyg_Interrupt::HANDLED;
}
 
// -------------------------------------------------------------------------
 
void Cyg_RealTimeClock::dsr(cyg_vector vector, cyg_ucount32 count, CYG_ADDRWORD data)
{
//    CYG_REPORT_FUNCTION();
 
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_DSR_LATENCY) && defined(HAL_CLOCK_LATENCY)
    if (measure_clock_latency) {
        cyg_int32 delta;
        HAL_CLOCK_READ((cyg_uint32 *)&delta);
        delta -= clock_dsr_start;
        // Note: Ignore a latency of <= 0 when finding min_clock_latency.
        if (delta > 0 ) {
            // Valid delta measured
            total_clock_dsr_latency += delta;
            total_clock_dsr_calls++;
            if (min_clock_dsr_latency > delta) min_clock_dsr_latency = delta;
            if (max_clock_dsr_latency < delta) max_clock_dsr_latency = delta;
        }
    }
#endif    
 
    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 == CYGINT_KERNEL_SCHEDULER_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
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.