//==========================================================================
|
//==========================================================================
|
//
|
//
|
// common/clock.cxx
|
// common/clock.cxx
|
//
|
//
|
// Clock class implementations
|
// Clock class implementations
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
//####ECOSGPLCOPYRIGHTBEGIN####
|
// -------------------------------------------
|
// -------------------------------------------
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
|
// Copyright (C) 2002 Jonathan Larmour
|
// Copyright (C) 2002 Jonathan Larmour
|
//
|
//
|
// eCos is free software; you can redistribute it and/or modify it under
|
// 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
|
// 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.
|
// 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
|
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
// for more details.
|
// for more details.
|
//
|
//
|
// You should have received a copy of the GNU General Public License along
|
// You should have received a copy of the GNU General Public License along
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// with eCos; if not, write to the Free Software Foundation, Inc.,
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
//
|
//
|
// As a special exception, if other files instantiate templates or use macros
|
// 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
|
// 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
|
// 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
|
// 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
|
// License. However the source code for this file must still be made available
|
// in accordance with section (3) of the GNU General Public License.
|
// 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 exception does not invalidate any other reasons why a work based on
|
// this file might be covered by the GNU General Public License.
|
// this file might be covered by the GNU General Public License.
|
//
|
//
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// at http://sources.redhat.com/ecos/ecos-license/
|
// -------------------------------------------
|
// -------------------------------------------
|
//####ECOSGPLCOPYRIGHTEND####
|
//####ECOSGPLCOPYRIGHTEND####
|
//==========================================================================
|
//==========================================================================
|
//#####DESCRIPTIONBEGIN####
|
//#####DESCRIPTIONBEGIN####
|
//
|
//
|
// Author(s): nickg
|
// Author(s): nickg
|
// Contributors: nickg
|
// Contributors: nickg
|
// Date: 1997-09-15
|
// Date: 1997-09-15
|
// Purpose: Clock class implementation
|
// Purpose: Clock class implementation
|
// Description: This file contains the definitions of the counter,
|
// Description: This file contains the definitions of the counter,
|
// clock and alarm class member functions that are common
|
// clock and alarm class member functions that are common
|
// to all clock implementations.
|
// to all clock implementations.
|
//
|
//
|
//####DESCRIPTIONEND####
|
//####DESCRIPTIONEND####
|
//
|
//
|
//==========================================================================
|
//==========================================================================
|
|
|
#include <pkgconf/kernel.h>
|
#include <pkgconf/kernel.h>
|
|
|
#include <cyg/kernel/ktypes.h> // base kernel types
|
#include <cyg/kernel/ktypes.h> // base kernel types
|
#include <cyg/infra/cyg_trac.h> // tracing macros
|
#include <cyg/infra/cyg_trac.h> // tracing macros
|
#include <cyg/infra/cyg_ass.h> // assertion macros
|
#include <cyg/infra/cyg_ass.h> // assertion macros
|
|
|
#include <cyg/kernel/clock.hxx> // our header
|
#include <cyg/kernel/clock.hxx> // our header
|
|
|
#include <cyg/kernel/sched.hxx> // scheduler definitions
|
#include <cyg/kernel/sched.hxx> // scheduler definitions
|
#include <cyg/kernel/thread.hxx> // thread definitions
|
#include <cyg/kernel/thread.hxx> // thread definitions
|
#include <cyg/kernel/intr.hxx> // interrupt definitions
|
#include <cyg/kernel/intr.hxx> // interrupt definitions
|
|
|
#include <cyg/kernel/sched.inl> // scheduler inlines
|
#include <cyg/kernel/sched.inl> // scheduler inlines
|
#include <cyg/kernel/clock.inl> // Clock inlines
|
#include <cyg/kernel/clock.inl> // Clock inlines
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
// Static variables
|
// Static variables
|
|
|
#ifdef CYGVAR_KERNEL_COUNTERS_CLOCK
|
#ifdef CYGVAR_KERNEL_COUNTERS_CLOCK
|
|
|
Cyg_Clock *Cyg_Clock::real_time_clock = NULL; // System real time clock
|
Cyg_Clock *Cyg_Clock::real_time_clock = NULL; // System real time clock
|
|
|
#endif
|
#endif
|
|
|
//==========================================================================
|
//==========================================================================
|
// Constructor for counter object
|
// Constructor for counter object
|
|
|
Cyg_Counter::Cyg_Counter(
|
Cyg_Counter::Cyg_Counter(
|
cyg_uint32 incr
|
cyg_uint32 incr
|
)
|
)
|
{
|
{
|
CYG_REPORT_FUNCTION();
|
CYG_REPORT_FUNCTION();
|
|
|
counter = 0;
|
counter = 0;
|
increment = incr;
|
increment = incr;
|
|
|
}
|
}
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
// Destructor for Counter object
|
// Destructor for Counter object
|
|
|
Cyg_Counter::~Cyg_Counter()
|
Cyg_Counter::~Cyg_Counter()
|
{
|
{
|
CYG_REPORT_FUNCTION();
|
CYG_REPORT_FUNCTION();
|
|
|
|
|
}
|
}
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
//
|
//
|
|
|
#ifdef CYGDBG_USE_ASSERTS
|
#ifdef CYGDBG_USE_ASSERTS
|
|
|
cyg_bool Cyg_Counter::check_this( cyg_assert_class_zeal zeal) const
|
cyg_bool Cyg_Counter::check_this( cyg_assert_class_zeal zeal) const
|
{
|
{
|
// check that we have a non-NULL pointer first
|
// check that we have a non-NULL pointer first
|
if( this == NULL ) return false;
|
if( this == NULL ) return false;
|
|
|
switch( zeal )
|
switch( zeal )
|
{
|
{
|
case cyg_system_test:
|
case cyg_system_test:
|
case cyg_extreme:
|
case cyg_extreme:
|
case cyg_thorough:
|
case cyg_thorough:
|
case cyg_quick:
|
case cyg_quick:
|
case cyg_trivial:
|
case cyg_trivial:
|
case cyg_none:
|
case cyg_none:
|
default:
|
default:
|
break;
|
break;
|
};
|
};
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
#endif
|
#endif
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
// Counter tick function
|
// Counter tick function
|
|
|
void Cyg_Counter::tick( cyg_uint32 ticks )
|
void Cyg_Counter::tick( cyg_uint32 ticks )
|
{
|
{
|
// CYG_REPORT_FUNCTION();
|
// CYG_REPORT_FUNCTION();
|
|
|
CYG_ASSERTCLASS( this, "Bad counter object" );
|
CYG_ASSERTCLASS( this, "Bad counter object" );
|
|
|
// Increment the counter in a loop so we process
|
// Increment the counter in a loop so we process
|
// each tick separately. This is easier than trying
|
// each tick separately. This is easier than trying
|
// to cope with a range of increments.
|
// to cope with a range of increments.
|
|
|
while( ticks-- )
|
while( ticks-- )
|
{
|
{
|
Cyg_Scheduler::lock();
|
Cyg_Scheduler::lock();
|
|
|
// increment the counter, note that it is
|
// increment the counter, note that it is
|
// allowed to wrap.
|
// allowed to wrap.
|
counter += increment;
|
counter += increment;
|
|
|
// now check for any expired alarms
|
// now check for any expired alarms
|
|
|
Cyg_Alarm_List *alarm_list_ptr; // pointer to list
|
Cyg_Alarm_List *alarm_list_ptr; // pointer to list
|
|
|
#if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST)
|
#if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST)
|
|
|
alarm_list_ptr = &alarm_list;
|
alarm_list_ptr = &alarm_list;
|
|
|
#elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST)
|
#elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST)
|
|
|
// With multiple lists, each one contains only the alarms
|
// With multiple lists, each one contains only the alarms
|
// that will expire at a given tick modulo the list number.
|
// that will expire at a given tick modulo the list number.
|
// So we only have a fraction of the alarms to check here.
|
// So we only have a fraction of the alarms to check here.
|
|
|
alarm_list_ptr = &(alarm_list[
|
alarm_list_ptr = &(alarm_list[
|
(counter/increment) % CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE ] );
|
(counter/increment) % CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE ] );
|
|
|
#else
|
#else
|
#error "No CYGIMP_KERNEL_COUNTERS_x_LIST config"
|
#error "No CYGIMP_KERNEL_COUNTERS_x_LIST config"
|
#endif
|
#endif
|
|
|
// Now that we have the list pointer, we can use common code for
|
// Now that we have the list pointer, we can use common code for
|
// both list oragnizations.
|
// both list oragnizations.
|
|
|
#ifdef CYGIMP_KERNEL_COUNTERS_SORT_LIST
|
#ifdef CYGIMP_KERNEL_COUNTERS_SORT_LIST
|
|
|
// With a sorted alarm list, we can simply pick alarms off the
|
// 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.
|
// front of the list until we find one that is in the future.
|
|
|
while( !alarm_list_ptr->empty() )
|
while( !alarm_list_ptr->empty() )
|
{
|
{
|
Cyg_Alarm *alarm = alarm_list_ptr->get_head();
|
Cyg_Alarm *alarm = alarm_list_ptr->get_head();
|
|
|
CYG_ASSERTCLASS(alarm, "Bad alarm in counter list" );
|
CYG_ASSERTCLASS(alarm, "Bad alarm in counter list" );
|
|
|
if( alarm->trigger <= counter )
|
if( alarm->trigger <= counter )
|
{
|
{
|
// remove alarm from list
|
// remove alarm from list
|
alarm_list_ptr->rem_head();
|
alarm_list_ptr->rem_head();
|
|
|
if( alarm->interval != 0 )
|
if( alarm->interval != 0 )
|
{
|
{
|
// The alarm has a retrigger interval.
|
// The alarm has a retrigger interval.
|
// Reset the trigger time and requeue
|
// Reset the trigger time and requeue
|
// the alarm.
|
// the alarm.
|
alarm->trigger += alarm->interval;
|
alarm->trigger += alarm->interval;
|
add_alarm( alarm );
|
add_alarm( alarm );
|
}
|
}
|
else alarm->enabled = false;
|
else alarm->enabled = false;
|
|
|
CYG_INSTRUMENT_ALARM( CALL, this, alarm );
|
CYG_INSTRUMENT_ALARM( CALL, this, alarm );
|
|
|
// call alarm function
|
// call alarm function
|
alarm->alarm(alarm, alarm->data);
|
alarm->alarm(alarm, alarm->data);
|
|
|
// all done, loop
|
// all done, loop
|
}
|
}
|
else break;
|
else break;
|
|
|
}
|
}
|
#else
|
#else
|
|
|
// With an unsorted list, we must scan the whole list for
|
// With an unsorted list, we must scan the whole list for
|
// candidates. We move the whole list to a temporary location
|
// candidates. We move the whole list to a temporary location
|
// before doing this so that we are not disturbed by new
|
// before doing this so that we are not disturbed by new
|
// alarms being added to the list. As we consider and
|
// alarms being added to the list. As we consider and
|
// eliminate alarms we put them onto the done_list and at the
|
// eliminate alarms we put them onto the done_list and at the
|
// end we then move it back to where it belongs.
|
// end we then move it back to where it belongs.
|
|
|
Cyg_Alarm_List done_list;
|
Cyg_Alarm_List done_list;
|
|
|
Cyg_Alarm_List alarm_list;
|
Cyg_Alarm_List alarm_list;
|
|
|
alarm_list.merge( *alarm_list_ptr );
|
alarm_list.merge( *alarm_list_ptr );
|
|
|
while( !alarm_list.empty() )
|
while( !alarm_list.empty() )
|
{
|
{
|
Cyg_Alarm *alarm = alarm_list.rem_head();
|
Cyg_Alarm *alarm = alarm_list.rem_head();
|
|
|
CYG_ASSERTCLASS(alarm, "Bad alarm in counter list" );
|
CYG_ASSERTCLASS(alarm, "Bad alarm in counter list" );
|
|
|
if( alarm->trigger <= counter )
|
if( alarm->trigger <= counter )
|
{
|
{
|
if( alarm->interval != 0 )
|
if( alarm->interval != 0 )
|
{
|
{
|
// The alarm has a retrigger interval.
|
// The alarm has a retrigger interval.
|
// Reset the trigger time and requeue
|
// Reset the trigger time and requeue
|
// the alarm.
|
// the alarm.
|
alarm->trigger += alarm->interval;
|
alarm->trigger += alarm->interval;
|
add_alarm( alarm );
|
add_alarm( alarm );
|
}
|
}
|
else alarm->enabled = false;
|
else alarm->enabled = false;
|
|
|
CYG_INSTRUMENT_ALARM( CALL, this, alarm );
|
CYG_INSTRUMENT_ALARM( CALL, this, alarm );
|
|
|
// call alarm function
|
// call alarm function
|
alarm->alarm(alarm, alarm->data);
|
alarm->alarm(alarm, alarm->data);
|
|
|
// all done, loop
|
// all done, loop
|
}
|
}
|
else
|
else
|
{
|
{
|
// add unused alarm to done list.
|
// add unused alarm to done list.
|
done_list.add_tail(alarm);
|
done_list.add_tail(alarm);
|
}
|
}
|
}
|
}
|
|
|
// Return done list to real list. If any alarms have been
|
// Return done list to real list. If any alarms have been
|
// added to the alarm list while we have been scanning then
|
// added to the alarm list while we have been scanning then
|
// the done list will be added behind them.
|
// the done list will be added behind them.
|
|
|
alarm_list_ptr->merge( done_list );
|
alarm_list_ptr->merge( done_list );
|
|
|
#endif
|
#endif
|
Cyg_Scheduler::unlock();
|
Cyg_Scheduler::unlock();
|
|
|
}
|
}
|
|
|
}
|
}
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
// Add an alarm to this counter
|
// Add an alarm to this counter
|
|
|
void Cyg_Counter::add_alarm( Cyg_Alarm *alarm )
|
void Cyg_Counter::add_alarm( Cyg_Alarm *alarm )
|
{
|
{
|
CYG_REPORT_FUNCTION();
|
CYG_REPORT_FUNCTION();
|
|
|
CYG_ASSERTCLASS( this, "Bad counter object" );
|
CYG_ASSERTCLASS( this, "Bad counter object" );
|
CYG_ASSERTCLASS( alarm, "Bad alarm passed" );
|
CYG_ASSERTCLASS( alarm, "Bad alarm passed" );
|
|
|
Cyg_Scheduler::lock();
|
Cyg_Scheduler::lock();
|
|
|
// set this now to allow an immediate handler call to manipulate
|
// set this now to allow an immediate handler call to manipulate
|
// this alarm sensibly.
|
// this alarm sensibly.
|
alarm->enabled = true;
|
alarm->enabled = true;
|
|
|
// Check here for an alarm that triggers now or in the past and
|
// Check here for an alarm that triggers now or in the past and
|
// call its alarm function immediately.
|
// call its alarm function immediately.
|
if( alarm->trigger <= counter )
|
if( alarm->trigger <= counter )
|
{
|
{
|
CYG_INSTRUMENT_ALARM( CALL, this, alarm );
|
CYG_INSTRUMENT_ALARM( CALL, this, alarm );
|
|
|
// call alarm function. Note that this is being
|
// call alarm function. Note that this is being
|
// called here before the add_alarm has returned.
|
// called here before the add_alarm has returned.
|
// Note that this function may disable the alarm.
|
// Note that this function may disable the alarm.
|
|
|
alarm->alarm(alarm, alarm->data);
|
alarm->alarm(alarm, alarm->data);
|
|
|
// Note that this extra check on alarm->enabled is in case the
|
// Note that this extra check on alarm->enabled is in case the
|
// handler function disables this alarm!
|
// handler function disables this alarm!
|
if( alarm->interval != 0 && alarm->enabled )
|
if( alarm->interval != 0 && alarm->enabled )
|
{
|
{
|
// The alarm has a retrigger interval.
|
// The alarm has a retrigger interval.
|
// Reset the trigger interval and drop
|
// Reset the trigger interval and drop
|
// through to queue it.
|
// through to queue it.
|
alarm->trigger += alarm->interval;
|
alarm->trigger += alarm->interval;
|
// ensure the next alarm time is in our future, and in phase
|
// ensure the next alarm time is in our future, and in phase
|
// with the original time requested.
|
// with the original time requested.
|
alarm->synchronize();
|
alarm->synchronize();
|
}
|
}
|
else
|
else
|
{
|
{
|
// The alarm is all done with, disable it
|
// The alarm is all done with, disable it
|
// unlock and return.
|
// unlock and return.
|
alarm->enabled = false;
|
alarm->enabled = false;
|
Cyg_Scheduler::unlock();
|
Cyg_Scheduler::unlock();
|
return;
|
return;
|
}
|
}
|
}
|
}
|
|
|
CYG_INSTRUMENT_ALARM( ADD, this, alarm );
|
CYG_INSTRUMENT_ALARM( ADD, this, alarm );
|
|
|
// Find the pointer to the relevant list _after_ a retrigger
|
// Find the pointer to the relevant list _after_ a retrigger
|
// alarm has been given its new trigger time.
|
// alarm has been given its new trigger time.
|
|
|
Cyg_Alarm_List *alarm_list_ptr; // pointer to list
|
Cyg_Alarm_List *alarm_list_ptr; // pointer to list
|
|
|
#if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST)
|
#if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST)
|
|
|
alarm_list_ptr = &alarm_list;
|
alarm_list_ptr = &alarm_list;
|
|
|
#elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST)
|
#elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST)
|
|
|
// Each alarm must go into the list that covers the tick that is
|
// 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
|
// going to happen _after_ the trigger time (or at it if trigger
|
// happens to fall on a tick.
|
// happens to fall on a tick.
|
|
|
alarm_list_ptr = &(alarm_list[
|
alarm_list_ptr = &(alarm_list[
|
((alarm->trigger+increment-1)/increment) %
|
((alarm->trigger+increment-1)/increment) %
|
CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE ] );
|
CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE ] );
|
|
|
#else
|
#else
|
#error "No CYGIMP_KERNEL_COUNTERS_x_LIST config"
|
#error "No CYGIMP_KERNEL_COUNTERS_x_LIST config"
|
#endif
|
#endif
|
|
|
#ifdef CYGIMP_KERNEL_COUNTERS_SORT_LIST
|
#ifdef CYGIMP_KERNEL_COUNTERS_SORT_LIST
|
|
|
// Now that we have the list pointer, we can use common code for
|
// Now that we have the list pointer, we can use common code for
|
// both list organizations.
|
// both list organizations.
|
|
|
Cyg_Alarm *list_alarm = alarm_list_ptr->get_head();
|
Cyg_Alarm *list_alarm = alarm_list_ptr->get_head();
|
|
|
if( list_alarm != NULL )
|
if( list_alarm != NULL )
|
{
|
{
|
do
|
do
|
{
|
{
|
CYG_ASSERTCLASS(list_alarm, "Bad alarm in counter list" );
|
CYG_ASSERTCLASS(list_alarm, "Bad alarm in counter list" );
|
|
|
// The alarms are in ascending trigger order. If we
|
// The alarms are in ascending trigger order. If we
|
// find an alarm that triggers later than us, we go
|
// find an alarm that triggers later than us, we go
|
// in front of it.
|
// in front of it.
|
|
|
if( list_alarm->trigger > alarm->trigger )
|
if( list_alarm->trigger > alarm->trigger )
|
{
|
{
|
alarm_list_ptr->insert( list_alarm, alarm );
|
alarm_list_ptr->insert( list_alarm, alarm );
|
goto add_alarm_unlock_return;
|
goto add_alarm_unlock_return;
|
}
|
}
|
|
|
list_alarm = list_alarm->get_next();
|
list_alarm = list_alarm->get_next();
|
|
|
} while( list_alarm != alarm_list_ptr->get_head() );
|
} while( list_alarm != alarm_list_ptr->get_head() );
|
// a lower or equal alarm time was not found, so drop through
|
// a lower or equal alarm time was not found, so drop through
|
// so it is added to the list tail
|
// so it is added to the list tail
|
}
|
}
|
alarm_list_ptr->add_tail( alarm );
|
alarm_list_ptr->add_tail( alarm );
|
|
|
add_alarm_unlock_return:
|
add_alarm_unlock_return:
|
#else
|
#else
|
|
|
alarm_list_ptr->add_tail( alarm );
|
alarm_list_ptr->add_tail( alarm );
|
|
|
#endif
|
#endif
|
|
|
Cyg_Scheduler::unlock();
|
Cyg_Scheduler::unlock();
|
}
|
}
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
// Remove an alarm from this counter
|
// Remove an alarm from this counter
|
|
|
void Cyg_Counter::rem_alarm( Cyg_Alarm *alarm )
|
void Cyg_Counter::rem_alarm( Cyg_Alarm *alarm )
|
{
|
{
|
CYG_REPORT_FUNCTION();
|
CYG_REPORT_FUNCTION();
|
|
|
CYG_ASSERTCLASS( this, "Bad counter object" );
|
CYG_ASSERTCLASS( this, "Bad counter object" );
|
CYG_ASSERTCLASS( alarm, "Bad alarm passed" );
|
CYG_ASSERTCLASS( alarm, "Bad alarm passed" );
|
|
|
Cyg_Alarm_List *alarm_list_ptr; // pointer to list
|
Cyg_Alarm_List *alarm_list_ptr; // pointer to list
|
|
|
#if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST)
|
#if defined(CYGIMP_KERNEL_COUNTERS_SINGLE_LIST)
|
|
|
alarm_list_ptr = &alarm_list;
|
alarm_list_ptr = &alarm_list;
|
|
|
#elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST)
|
#elif defined(CYGIMP_KERNEL_COUNTERS_MULTI_LIST)
|
|
|
alarm_list_ptr = &(alarm_list[
|
alarm_list_ptr = &(alarm_list[
|
((alarm->trigger+increment-1)/increment) %
|
((alarm->trigger+increment-1)/increment) %
|
CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE ] );
|
CYGNUM_KERNEL_COUNTERS_MULTI_LIST_SIZE ] );
|
|
|
#else
|
#else
|
#error "No CYGIMP_KERNEL_COUNTERS_x_LIST config"
|
#error "No CYGIMP_KERNEL_COUNTERS_x_LIST config"
|
#endif
|
#endif
|
|
|
// Now that we have the list pointer, we can use common code for
|
// Now that we have the list pointer, we can use common code for
|
// both list organizations.
|
// both list organizations.
|
|
|
Cyg_Scheduler::lock();
|
Cyg_Scheduler::lock();
|
|
|
CYG_INSTRUMENT_ALARM( REM, this, alarm );
|
CYG_INSTRUMENT_ALARM( REM, this, alarm );
|
|
|
alarm_list_ptr->remove( alarm );
|
alarm_list_ptr->remove( alarm );
|
|
|
alarm->enabled = false;
|
alarm->enabled = false;
|
|
|
Cyg_Scheduler::unlock();
|
Cyg_Scheduler::unlock();
|
}
|
}
|
|
|
//==========================================================================
|
//==========================================================================
|
// Constructor for clock object
|
// Constructor for clock object
|
|
|
Cyg_Clock::Cyg_Clock(
|
Cyg_Clock::Cyg_Clock(
|
cyg_resolution res
|
cyg_resolution res
|
)
|
)
|
{
|
{
|
CYG_REPORT_FUNCTION();
|
CYG_REPORT_FUNCTION();
|
|
|
resolution = res;
|
resolution = res;
|
}
|
}
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
// Destructor for Clock objects
|
// Destructor for Clock objects
|
|
|
Cyg_Clock::~Cyg_Clock()
|
Cyg_Clock::~Cyg_Clock()
|
{
|
{
|
CYG_REPORT_FUNCTION();
|
CYG_REPORT_FUNCTION();
|
|
|
}
|
}
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
//
|
//
|
|
|
#ifdef CYGDBG_USE_ASSERTS
|
#ifdef CYGDBG_USE_ASSERTS
|
|
|
cyg_bool Cyg_Clock::check_this( cyg_assert_class_zeal zeal) const
|
cyg_bool Cyg_Clock::check_this( cyg_assert_class_zeal zeal) const
|
{
|
{
|
// check that we have a non-NULL pointer first
|
// check that we have a non-NULL pointer first
|
if( this == NULL ) return false;
|
if( this == NULL ) return false;
|
|
|
switch( zeal )
|
switch( zeal )
|
{
|
{
|
case cyg_system_test:
|
case cyg_system_test:
|
case cyg_extreme:
|
case cyg_extreme:
|
case cyg_thorough:
|
case cyg_thorough:
|
case cyg_quick:
|
case cyg_quick:
|
case cyg_trivial:
|
case cyg_trivial:
|
case cyg_none:
|
case cyg_none:
|
default:
|
default:
|
break;
|
break;
|
};
|
};
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
#endif
|
#endif
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
//
|
//
|
// Clock Converters: split a rational into 4 factors to try to prevent
|
// Clock Converters: split a rational into 4 factors to try to prevent
|
// overflow whilst retaining reasonable accuracy.
|
// overflow whilst retaining reasonable accuracy.
|
//
|
//
|
// typically we get numbers like 1,000,000 for ns_per and
|
// typically we get numbers like 1,000,000 for ns_per and
|
// 100 and 1,000,000,000 for the dividend and divisor.
|
// 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.
|
// So we want answers like 1/10 and 10/1 out of these routines.
|
|
|
static void construct_converter( Cyg_Clock::converter *pcc,
|
static void construct_converter( Cyg_Clock::converter *pcc,
|
cyg_uint64 m1, cyg_uint64 d1,
|
cyg_uint64 m1, cyg_uint64 d1,
|
cyg_uint64 m2, cyg_uint64 d2 )
|
cyg_uint64 m2, cyg_uint64 d2 )
|
{
|
{
|
cyg_uint64 upper, lower;
|
cyg_uint64 upper, lower;
|
unsigned int i;
|
unsigned int i;
|
static cyg_uint16 primes[] = {
|
static cyg_uint16 primes[] = {
|
3,5,7,11,13,17,19,23,29,31,37,41,43,47,
|
3,5,7,11,13,17,19,23,29,31,37,41,43,47,
|
53,59,61,67,71,73,79,83,89,97,
|
53,59,61,67,71,73,79,83,89,97,
|
101,103,107,109,113,127,131,137,139,149,
|
101,103,107,109,113,127,131,137,139,149,
|
151,157,163,167,173,179,181,191,193,197,199,
|
151,157,163,167,173,179,181,191,193,197,199,
|
239, // for 1,111,111
|
239, // for 1,111,111
|
541, // for 10,101,011
|
541, // for 10,101,011
|
1667, // for 8,333,333
|
1667, // for 8,333,333
|
};
|
};
|
|
|
int rounding = 0;
|
int rounding = 0;
|
|
|
// Here we assume that our workings will fit in a 64; the point is to
|
// 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.
|
// allow calculations with a number of ticks that may be large.
|
upper = m1 * m2;
|
upper = m1 * m2;
|
lower = d1 * d2;
|
lower = d1 * d2;
|
#ifdef CYGDBG_USE_ASSERTS
|
#ifdef CYGDBG_USE_ASSERTS
|
cyg_uint64 save_upper = upper;
|
cyg_uint64 save_upper = upper;
|
cyg_uint64 save_lower = lower;
|
cyg_uint64 save_lower = lower;
|
#endif
|
#endif
|
|
|
retry_rounding:
|
retry_rounding:
|
// First strip out common powers of 2
|
// First strip out common powers of 2
|
while ( (0 == (1 & upper)) && ( 0 == (1 & lower)) ) {
|
while ( (0 == (1 & upper)) && ( 0 == (1 & lower)) ) {
|
upper >>= 1;
|
upper >>= 1;
|
lower >>= 1;
|
lower >>= 1;
|
}
|
}
|
|
|
// then common factors - use lazy table above
|
// then common factors - use lazy table above
|
for ( i = 0 ; i < (sizeof( primes )/sizeof( primes[0] )); i++ ) {
|
for ( i = 0 ; i < (sizeof( primes )/sizeof( primes[0] )); i++ ) {
|
cyg_uint64 j, k, p = (cyg_uint64)(primes[i]);
|
cyg_uint64 j, k, p = (cyg_uint64)(primes[i]);
|
j = upper / p;
|
j = upper / p;
|
while ( j * p == upper ) {
|
while ( j * p == upper ) {
|
k = lower / p;
|
k = lower / p;
|
if ( k * p != lower )
|
if ( k * p != lower )
|
break;
|
break;
|
upper = j;
|
upper = j;
|
lower = k;
|
lower = k;
|
j = upper / p;
|
j = upper / p;
|
}
|
}
|
}
|
}
|
|
|
m1 = upper;
|
m1 = upper;
|
d1 = lower;
|
d1 = lower;
|
m2 = 1;
|
m2 = 1;
|
d2 = 1;
|
d2 = 1;
|
|
|
if ( m1 > 0x10000 ) {
|
if ( m1 > 0x10000 ) {
|
// only bother if there are more than 16 bits consumed here
|
// only bother if there are more than 16 bits consumed here
|
|
|
// now move powers of 2 from d1 to d2
|
// now move powers of 2 from d1 to d2
|
// keeping them the same order of magnitude
|
// keeping them the same order of magnitude
|
while ( (0 == (1 & d1)) && (d2 < d1) ) {
|
while ( (0 == (1 & d1)) && (d2 < d1) ) {
|
d1 >>= 1;
|
d1 >>= 1;
|
d2 <<= 1;
|
d2 <<= 1;
|
}
|
}
|
|
|
// and factors from the table - go too far, if anything
|
// and factors from the table - go too far, if anything
|
int cont = (d2 < d1);
|
int cont = (d2 < d1);
|
for ( i = 0 ; cont && (i < (sizeof( primes )/sizeof( primes[0] ))); i++ ) {
|
for ( i = 0 ; cont && (i < (sizeof( primes )/sizeof( primes[0] ))); i++ ) {
|
cyg_uint64 k, p = (cyg_uint64)(primes[i]);
|
cyg_uint64 k, p = (cyg_uint64)(primes[i]);
|
k = d1 / p;
|
k = d1 / p;
|
while ( cont && ((k * p) == d1) ) {
|
while ( cont && ((k * p) == d1) ) {
|
// we can extract a prime
|
// we can extract a prime
|
d1 = k;
|
d1 = k;
|
d2 *= p;
|
d2 *= p;
|
k = d1 / p;
|
k = d1 / p;
|
cont = (d2 < d1);
|
cont = (d2 < d1);
|
}
|
}
|
}
|
}
|
|
|
// move powers of 2 from m1 to m2 so long as we do not go less than 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)) ) {
|
while ( (0 == (1 & m1)) && (m2 < m1) && (m1 > (d1 << 5)) ) {
|
m1 >>= 1;
|
m1 >>= 1;
|
m2 <<= 1;
|
m2 <<= 1;
|
if ( m1 < 0x10000 )
|
if ( m1 < 0x10000 )
|
break;
|
break;
|
}
|
}
|
|
|
// and factors from the table - ensure m1 stays well larger than d1
|
// and factors from the table - ensure m1 stays well larger than d1
|
cont = ((m2 < m1) && (m1 > (d1 << 4)) && (m1 > 0x10000));
|
cont = ((m2 < m1) && (m1 > (d1 << 4)) && (m1 > 0x10000));
|
for ( i = 0 ; cont && (i < (sizeof( primes )/sizeof( primes[0] ))); i++ ) {
|
for ( i = 0 ; cont && (i < (sizeof( primes )/sizeof( primes[0] ))); i++ ) {
|
cyg_uint64 k, p = (cyg_uint64)(primes[i]);
|
cyg_uint64 k, p = (cyg_uint64)(primes[i]);
|
k = m1 / p;
|
k = m1 / p;
|
cont = cont && (k > (d1 << 4) && (k > 0x10000));
|
cont = cont && (k > (d1 << 4) && (k > 0x10000));
|
while ( cont && ((k * p) == m1) ) {
|
while ( cont && ((k * p) == m1) ) {
|
// we can extract a prime
|
// we can extract a prime
|
m1 = k;
|
m1 = k;
|
m2 *= p;
|
m2 *= p;
|
k = m1 / p; // examine k for getting too small
|
k = m1 / p; // examine k for getting too small
|
cont = ((m2 < m1) && (k > (d1 << 4)) && (k > 0x10000));
|
cont = ((m2 < m1) && (k > (d1 << 4)) && (k > 0x10000));
|
}
|
}
|
}
|
}
|
|
|
// if, after all that, m1 odd and unchanged, and too large,
|
// if, after all that, m1 odd and unchanged, and too large,
|
// decrement it just the once and try again: then try it
|
// decrement it just the once and try again: then try it
|
// incremented once.
|
// incremented once.
|
if ( (m1 & 1) && (m1 == upper) && (m1 > 0x10000) && (rounding < 2) ) {
|
if ( (m1 & 1) && (m1 == upper) && (m1 > 0x10000) && (rounding < 2) ) {
|
CYG_ASSERT( 1 == m2, "m2 should be 1 to try rounding" );
|
CYG_ASSERT( 1 == m2, "m2 should be 1 to try rounding" );
|
m1--;
|
m1--;
|
upper = m1;
|
upper = m1;
|
rounding++;
|
rounding++;
|
goto retry_rounding;
|
goto retry_rounding;
|
}
|
}
|
// likewise for d1 - each of the pair can be odd only once each
|
// likewise for d1 - each of the pair can be odd only once each
|
if ( (d1 & 1) && (d1 == lower) && (d1 > 0x10000) && (rounding < 2) ) {
|
if ( (d1 & 1) && (d1 == lower) && (d1 > 0x10000) && (rounding < 2) ) {
|
CYG_ASSERT( 1 == d2, "d2 should be 1 to try rounding" );
|
CYG_ASSERT( 1 == d2, "d2 should be 1 to try rounding" );
|
d1--;
|
d1--;
|
lower = d1;
|
lower = d1;
|
rounding++;
|
rounding++;
|
goto retry_rounding;
|
goto retry_rounding;
|
}
|
}
|
}
|
}
|
|
|
CYG_ASSERT( 0 != m1, "m1 zero" );
|
CYG_ASSERT( 0 != m1, "m1 zero" );
|
CYG_ASSERT( 0 != m2, "m2 zero" );
|
CYG_ASSERT( 0 != m2, "m2 zero" );
|
CYG_ASSERT( 0 != d1, "d1 zero" );
|
CYG_ASSERT( 0 != d1, "d1 zero" );
|
CYG_ASSERT( 0 != d2, "d2 zero" );
|
CYG_ASSERT( 0 != d2, "d2 zero" );
|
CYG_ASSERT( rounding || save_upper/save_lower == (m1 * m2)/(d1 * d2),
|
CYG_ASSERT( rounding || save_upper/save_lower == (m1 * m2)/(d1 * d2),
|
"Unequal in forwards direction" );
|
"Unequal in forwards direction" );
|
CYG_ASSERT( rounding || save_lower/save_upper == (d1 * d2)/(m1 * m2),
|
CYG_ASSERT( rounding || save_lower/save_upper == (d1 * d2)/(m1 * m2),
|
"Unequal in reverse direction" );
|
"Unequal in reverse direction" );
|
|
|
pcc->mul1 = m1;
|
pcc->mul1 = m1;
|
pcc->div1 = d1;
|
pcc->div1 = d1;
|
pcc->mul2 = m2;
|
pcc->mul2 = m2;
|
pcc->div2 = d2;
|
pcc->div2 = d2;
|
}
|
}
|
|
|
// other to clocks is (other * ns_per * dividend / divisor)
|
// other to clocks is (other * ns_per * dividend / divisor)
|
void Cyg_Clock::get_other_to_clock_converter(
|
void Cyg_Clock::get_other_to_clock_converter(
|
cyg_uint64 ns_per_other_tick,
|
cyg_uint64 ns_per_other_tick,
|
struct converter *pcc )
|
struct converter *pcc )
|
{
|
{
|
construct_converter( pcc,
|
construct_converter( pcc,
|
ns_per_other_tick, 1,
|
ns_per_other_tick, 1,
|
resolution.divisor, resolution.dividend );
|
resolution.divisor, resolution.dividend );
|
}
|
}
|
|
|
// clocks to other is (ticks * divisor / dividend / ns_per)
|
// clocks to other is (ticks * divisor / dividend / ns_per)
|
void Cyg_Clock::get_clock_to_other_converter(
|
void Cyg_Clock::get_clock_to_other_converter(
|
cyg_uint64 ns_per_other_tick,
|
cyg_uint64 ns_per_other_tick,
|
struct converter *pcc )
|
struct converter *pcc )
|
{
|
{
|
construct_converter( pcc,
|
construct_converter( pcc,
|
1, ns_per_other_tick,
|
1, ns_per_other_tick,
|
resolution.dividend, resolution.divisor );
|
resolution.dividend, resolution.divisor );
|
}
|
}
|
|
|
|
|
//==========================================================================
|
//==========================================================================
|
// Constructor for alarm object
|
// Constructor for alarm object
|
|
|
Cyg_Alarm::Cyg_Alarm(
|
Cyg_Alarm::Cyg_Alarm(
|
Cyg_Counter *c, // Attached to this counter
|
Cyg_Counter *c, // Attached to this counter
|
cyg_alarm_fn *a, // Call-back function
|
cyg_alarm_fn *a, // Call-back function
|
CYG_ADDRWORD d // Call-back data
|
CYG_ADDRWORD d // Call-back data
|
)
|
)
|
{
|
{
|
CYG_REPORT_FUNCTION();
|
CYG_REPORT_FUNCTION();
|
|
|
counter = c;
|
counter = c;
|
alarm = a;
|
alarm = a;
|
data = d;
|
data = d;
|
trigger = 0;
|
trigger = 0;
|
interval = 0;
|
interval = 0;
|
enabled = false;
|
enabled = false;
|
|
|
}
|
}
|
|
|
Cyg_Alarm::Cyg_Alarm(){}
|
Cyg_Alarm::Cyg_Alarm(){}
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
// Destructor
|
// Destructor
|
|
|
Cyg_Alarm::~Cyg_Alarm()
|
Cyg_Alarm::~Cyg_Alarm()
|
{
|
{
|
CYG_REPORT_FUNCTION();
|
CYG_REPORT_FUNCTION();
|
|
|
disable();
|
disable();
|
}
|
}
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
//
|
//
|
|
|
#ifdef CYGDBG_USE_ASSERTS
|
#ifdef CYGDBG_USE_ASSERTS
|
|
|
cyg_bool Cyg_Alarm::check_this( cyg_assert_class_zeal zeal) const
|
cyg_bool Cyg_Alarm::check_this( cyg_assert_class_zeal zeal) const
|
{
|
{
|
// check that we have a non-NULL pointer first
|
// check that we have a non-NULL pointer first
|
if( this == NULL ) return false;
|
if( this == NULL ) return false;
|
|
|
switch( zeal )
|
switch( zeal )
|
{
|
{
|
case cyg_system_test:
|
case cyg_system_test:
|
case cyg_extreme:
|
case cyg_extreme:
|
case cyg_thorough:
|
case cyg_thorough:
|
if( trigger != 0 && !enabled ) return false;
|
if( trigger != 0 && !enabled ) return false;
|
case cyg_quick:
|
case cyg_quick:
|
case cyg_trivial:
|
case cyg_trivial:
|
case cyg_none:
|
case cyg_none:
|
default:
|
default:
|
break;
|
break;
|
};
|
};
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
#endif
|
#endif
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
// Initialize Alarm and enable
|
// Initialize Alarm and enable
|
|
|
void Cyg_Alarm::initialize(
|
void Cyg_Alarm::initialize(
|
cyg_tick_count t, // Absolute trigger time
|
cyg_tick_count t, // Absolute trigger time
|
cyg_tick_count i // Relative retrigger interval
|
cyg_tick_count i // Relative retrigger interval
|
)
|
)
|
{
|
{
|
CYG_REPORT_FUNCTION();
|
CYG_REPORT_FUNCTION();
|
|
|
// If already enabled, remove from counter
|
// If already enabled, remove from counter
|
|
|
if( enabled ) counter->rem_alarm(this);
|
if( enabled ) counter->rem_alarm(this);
|
|
|
CYG_INSTRUMENT_ALARM( INIT, this, 0 );
|
CYG_INSTRUMENT_ALARM( INIT, this, 0 );
|
CYG_INSTRUMENT_ALARM( TRIGGER,
|
CYG_INSTRUMENT_ALARM( TRIGGER,
|
((cyg_uint32 *)&t)[0],
|
((cyg_uint32 *)&t)[0],
|
((cyg_uint32 *)&t)[1] );
|
((cyg_uint32 *)&t)[1] );
|
CYG_INSTRUMENT_ALARM( INTERVAL,
|
CYG_INSTRUMENT_ALARM( INTERVAL,
|
((cyg_uint32 *)&i)[0],
|
((cyg_uint32 *)&i)[0],
|
((cyg_uint32 *)&i)[1] );
|
((cyg_uint32 *)&i)[1] );
|
|
|
trigger = t;
|
trigger = t;
|
interval = i;
|
interval = i;
|
|
|
counter->add_alarm(this);
|
counter->add_alarm(this);
|
}
|
}
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
// Synchronize with a past alarm stream that had been disabled,
|
// Synchronize with a past alarm stream that had been disabled,
|
// bring past times into synch, and the like.
|
// bring past times into synch, and the like.
|
|
|
void
|
void
|
Cyg_Alarm::synchronize( void )
|
Cyg_Alarm::synchronize( void )
|
{
|
{
|
if( interval != 0 ) {
|
if( interval != 0 ) {
|
// This expression sets the trigger to the next whole interval
|
// This expression sets the trigger to the next whole interval
|
// at or after the current time. This means that alarms will
|
// at or after the current time. This means that alarms will
|
// continue at the same intervals as if they had never been
|
// continue at the same intervals as if they had never been
|
// disabled. The alternative would be to just set trigger to
|
// disabled. The alternative would be to just set trigger to
|
// (counter->counter + interval), but this is less satisfying
|
// (counter->counter + interval), but this is less satisfying
|
// than preserving the original intervals. That behaviour can
|
// than preserving the original intervals. That behaviour can
|
// always be obtained by using initialize() rather than
|
// always be obtained by using initialize() rather than
|
// enable(), while the current behaviour would be more
|
// enable(), while the current behaviour would be more
|
// difficult to achieve that way.
|
// difficult to achieve that way.
|
cyg_tick_count d;
|
cyg_tick_count d;
|
d = counter->current_value() + interval - trigger;
|
d = counter->current_value() + interval - trigger;
|
if ( d > interval ) {
|
if ( d > interval ) {
|
// then trigger was in the past, so resynchronize
|
// then trigger was in the past, so resynchronize
|
trigger += interval * ((d - 1) / interval );
|
trigger += interval * ((d - 1) / interval );
|
}
|
}
|
// otherwise, we were just set up, so no worries.
|
// otherwise, we were just set up, so no worries.
|
}
|
}
|
}
|
}
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
// Ensure alarm enabled
|
// Ensure alarm enabled
|
|
|
void Cyg_Alarm::enable()
|
void Cyg_Alarm::enable()
|
{
|
{
|
if( !enabled )
|
if( !enabled )
|
{
|
{
|
// ensure the alarm time is in our future:
|
// ensure the alarm time is in our future:
|
synchronize();
|
synchronize();
|
enabled = true;
|
enabled = true;
|
counter->add_alarm(this);
|
counter->add_alarm(this);
|
}
|
}
|
}
|
}
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
// Get the current time values from the alarm
|
// Get the current time values from the alarm
|
|
|
void Cyg_Alarm::get_times(
|
void Cyg_Alarm::get_times(
|
cyg_tick_count *t, // Next trigger time
|
cyg_tick_count *t, // Next trigger time
|
cyg_tick_count *i // Current interval
|
cyg_tick_count *i // Current interval
|
)
|
)
|
{
|
{
|
// Lock the scheduler while we do this to avoid
|
// Lock the scheduler while we do this to avoid
|
// race conditions.
|
// race conditions.
|
Cyg_Scheduler::lock();
|
Cyg_Scheduler::lock();
|
|
|
if( t != NULL ) *t = trigger;
|
if( t != NULL ) *t = trigger;
|
if( i != NULL ) *i = interval;
|
if( i != NULL ) *i = interval;
|
|
|
Cyg_Scheduler::unlock();
|
Cyg_Scheduler::unlock();
|
}
|
}
|
|
|
//==========================================================================
|
//==========================================================================
|
// System clock object
|
// System clock object
|
|
|
#ifdef CYGVAR_KERNEL_COUNTERS_CLOCK
|
#ifdef CYGVAR_KERNEL_COUNTERS_CLOCK
|
|
|
class Cyg_RealTimeClock
|
class Cyg_RealTimeClock
|
: public Cyg_Clock
|
: public Cyg_Clock
|
{
|
{
|
Cyg_Interrupt interrupt;
|
Cyg_Interrupt interrupt;
|
|
|
static cyg_uint32 isr(cyg_vector vector, CYG_ADDRWORD data);
|
static cyg_uint32 isr(cyg_vector vector, CYG_ADDRWORD data);
|
|
|
static void dsr(cyg_vector vector, cyg_ucount32 count, CYG_ADDRWORD data);
|
static void dsr(cyg_vector vector, cyg_ucount32 count, CYG_ADDRWORD data);
|
|
|
Cyg_RealTimeClock();
|
Cyg_RealTimeClock();
|
|
|
static Cyg_RealTimeClock rtc;
|
static Cyg_RealTimeClock rtc;
|
};
|
};
|
|
|
Cyg_Clock::cyg_resolution rtc_resolution = CYGNUM_KERNEL_COUNTERS_RTC_RESOLUTION;
|
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 __attribute__((init_priority (1)));
|
|
|
Cyg_RealTimeClock Cyg_RealTimeClock::rtc CYG_INIT_PRIORITY( CLOCK );
|
Cyg_RealTimeClock Cyg_RealTimeClock::rtc CYG_INIT_PRIORITY( CLOCK );
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
|
|
Cyg_RealTimeClock::Cyg_RealTimeClock()
|
Cyg_RealTimeClock::Cyg_RealTimeClock()
|
: Cyg_Clock(rtc_resolution),
|
: Cyg_Clock(rtc_resolution),
|
interrupt(CYGNUM_HAL_INTERRUPT_RTC, 1, (CYG_ADDRWORD)this, isr, dsr)
|
interrupt(CYGNUM_HAL_INTERRUPT_RTC, 1, (CYG_ADDRWORD)this, isr, dsr)
|
{
|
{
|
CYG_REPORT_FUNCTION();
|
CYG_REPORT_FUNCTION();
|
|
|
HAL_CLOCK_INITIALIZE( CYGNUM_KERNEL_COUNTERS_RTC_PERIOD );
|
HAL_CLOCK_INITIALIZE( CYGNUM_KERNEL_COUNTERS_RTC_PERIOD );
|
|
|
interrupt.attach();
|
interrupt.attach();
|
|
|
interrupt.unmask_interrupt(CYGNUM_HAL_INTERRUPT_RTC);
|
interrupt.unmask_interrupt(CYGNUM_HAL_INTERRUPT_RTC);
|
|
|
Cyg_Clock::real_time_clock = this;
|
Cyg_Clock::real_time_clock = this;
|
}
|
}
|
|
|
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_LATENCY) && defined(HAL_CLOCK_LATENCY)
|
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_LATENCY) && defined(HAL_CLOCK_LATENCY)
|
cyg_tick_count total_clock_latency, total_clock_interrupts;
|
cyg_tick_count total_clock_latency, total_clock_interrupts;
|
cyg_int32 min_clock_latency = 0x7FFFFFFF;
|
cyg_int32 min_clock_latency = 0x7FFFFFFF;
|
cyg_int32 max_clock_latency = 0;
|
cyg_int32 max_clock_latency = 0;
|
bool measure_clock_latency = false;
|
bool measure_clock_latency = false;
|
#endif
|
#endif
|
|
|
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_DSR_LATENCY)
|
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_DSR_LATENCY)
|
cyg_tick_count total_clock_dsr_latency, total_clock_dsr_calls;
|
cyg_tick_count total_clock_dsr_latency, total_clock_dsr_calls;
|
cyg_int32 min_clock_dsr_latency = 0x7FFFFFFF;
|
cyg_int32 min_clock_dsr_latency = 0x7FFFFFFF;
|
cyg_int32 max_clock_dsr_latency = 0;
|
cyg_int32 max_clock_dsr_latency = 0;
|
cyg_uint32 clock_dsr_start = 0;
|
cyg_uint32 clock_dsr_start = 0;
|
#endif
|
#endif
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
|
|
cyg_uint32 Cyg_RealTimeClock::isr(cyg_vector vector, CYG_ADDRWORD data)
|
cyg_uint32 Cyg_RealTimeClock::isr(cyg_vector vector, CYG_ADDRWORD data)
|
{
|
{
|
// CYG_REPORT_FUNCTION();
|
// CYG_REPORT_FUNCTION();
|
|
|
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_LATENCY) && defined(HAL_CLOCK_LATENCY)
|
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_LATENCY) && defined(HAL_CLOCK_LATENCY)
|
if (measure_clock_latency) {
|
if (measure_clock_latency) {
|
cyg_int32 delta;
|
cyg_int32 delta;
|
HAL_CLOCK_LATENCY(&delta);
|
HAL_CLOCK_LATENCY(&delta);
|
// Note: Ignore a latency of 0 when finding min_clock_latency.
|
// Note: Ignore a latency of 0 when finding min_clock_latency.
|
if (delta > 0) {
|
if (delta > 0) {
|
// Valid delta measured
|
// Valid delta measured
|
total_clock_latency += delta;
|
total_clock_latency += delta;
|
total_clock_interrupts++;
|
total_clock_interrupts++;
|
if (min_clock_latency > delta) min_clock_latency = delta;
|
if (min_clock_latency > delta) min_clock_latency = delta;
|
if (max_clock_latency < delta) max_clock_latency = delta;
|
if (max_clock_latency < delta) max_clock_latency = delta;
|
}
|
}
|
}
|
}
|
#endif
|
#endif
|
|
|
CYG_INSTRUMENT_CLOCK( ISR, 0, 0);
|
CYG_INSTRUMENT_CLOCK( ISR, 0, 0);
|
|
|
HAL_CLOCK_RESET( CYGNUM_HAL_INTERRUPT_RTC, CYGNUM_KERNEL_COUNTERS_RTC_PERIOD );
|
HAL_CLOCK_RESET( CYGNUM_HAL_INTERRUPT_RTC, CYGNUM_KERNEL_COUNTERS_RTC_PERIOD );
|
|
|
Cyg_Interrupt::acknowledge_interrupt(CYGNUM_HAL_INTERRUPT_RTC);
|
Cyg_Interrupt::acknowledge_interrupt(CYGNUM_HAL_INTERRUPT_RTC);
|
|
|
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_DSR_LATENCY)
|
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_DSR_LATENCY)
|
HAL_CLOCK_READ(&clock_dsr_start);
|
HAL_CLOCK_READ(&clock_dsr_start);
|
#endif
|
#endif
|
return Cyg_Interrupt::CALL_DSR|Cyg_Interrupt::HANDLED;
|
return Cyg_Interrupt::CALL_DSR|Cyg_Interrupt::HANDLED;
|
}
|
}
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
|
|
void Cyg_RealTimeClock::dsr(cyg_vector vector, cyg_ucount32 count, CYG_ADDRWORD data)
|
void Cyg_RealTimeClock::dsr(cyg_vector vector, cyg_ucount32 count, CYG_ADDRWORD data)
|
{
|
{
|
// CYG_REPORT_FUNCTION();
|
// CYG_REPORT_FUNCTION();
|
|
|
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_DSR_LATENCY) && defined(HAL_CLOCK_LATENCY)
|
#if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_DSR_LATENCY) && defined(HAL_CLOCK_LATENCY)
|
if (measure_clock_latency) {
|
if (measure_clock_latency) {
|
cyg_int32 delta;
|
cyg_int32 delta;
|
HAL_CLOCK_READ((cyg_uint32 *)&delta);
|
HAL_CLOCK_READ((cyg_uint32 *)&delta);
|
delta -= clock_dsr_start;
|
delta -= clock_dsr_start;
|
// Note: Ignore a latency of <= 0 when finding min_clock_latency.
|
// Note: Ignore a latency of <= 0 when finding min_clock_latency.
|
if (delta > 0 ) {
|
if (delta > 0 ) {
|
// Valid delta measured
|
// Valid delta measured
|
total_clock_dsr_latency += delta;
|
total_clock_dsr_latency += delta;
|
total_clock_dsr_calls++;
|
total_clock_dsr_calls++;
|
if (min_clock_dsr_latency > delta) min_clock_dsr_latency = delta;
|
if (min_clock_dsr_latency > delta) min_clock_dsr_latency = delta;
|
if (max_clock_dsr_latency < delta) max_clock_dsr_latency = delta;
|
if (max_clock_dsr_latency < delta) max_clock_dsr_latency = delta;
|
}
|
}
|
}
|
}
|
#endif
|
#endif
|
|
|
Cyg_RealTimeClock *rtc = (Cyg_RealTimeClock *)data;
|
Cyg_RealTimeClock *rtc = (Cyg_RealTimeClock *)data;
|
|
|
CYG_INSTRUMENT_CLOCK( TICK_START,
|
CYG_INSTRUMENT_CLOCK( TICK_START,
|
rtc->current_value_lo(),
|
rtc->current_value_lo(),
|
rtc->current_value_hi());
|
rtc->current_value_hi());
|
|
|
rtc->tick( count );
|
rtc->tick( count );
|
|
|
#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE
|
#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE
|
#if 0 == CYGINT_KERNEL_SCHEDULER_UNIQUE_PRIORITIES
|
#if 0 == CYGINT_KERNEL_SCHEDULER_UNIQUE_PRIORITIES
|
|
|
// If timeslicing is enabled, call the scheduler to
|
// If timeslicing is enabled, call the scheduler to
|
// handle it. But not if we have unique priorities.
|
// handle it. But not if we have unique priorities.
|
|
|
Cyg_Scheduler::scheduler.timeslice();
|
Cyg_Scheduler::scheduler.timeslice();
|
|
|
#endif
|
#endif
|
#endif
|
#endif
|
|
|
CYG_INSTRUMENT_CLOCK( TICK_END,
|
CYG_INSTRUMENT_CLOCK( TICK_END,
|
rtc->current_value_lo(),
|
rtc->current_value_lo(),
|
rtc->current_value_hi());
|
rtc->current_value_hi());
|
|
|
}
|
}
|
|
|
#endif
|
#endif
|
|
|
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
// EOF common/clock.cxx
|
// EOF common/clock.cxx
|
|
|