📄 clock.cxx
字号:
//
// 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();
Cyg_Scheduler::lock();
// 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);
Cyg_Scheduler::unlock();
}
// -------------------------------------------------------------------------
// 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()
{
Cyg_Scheduler::lock();
if( !enabled )
{
// ensure the alarm time is in our future:
synchronize();
enabled = true;
counter->add_alarm(this);
}
Cyg_Scheduler::unlock();
}
// -------------------------------------------------------------------------
// Ensure alarm disabled
void Cyg_Alarm::disable()
{
Cyg_Scheduler::lock();
if( enabled ) counter->rem_alarm(this);
Cyg_Scheduler::unlock();
}
// -------------------------------------------------------------------------
// 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -