root/trunk/libffado/src/libieee1394/CycleTimerHelper.cpp

Revision 940, 25.0 kB (checked in by ppalmers, 16 years ago)

use RT watchdog for 1394 service threads

Line 
1 /*
2  * Copyright (C) 2005-2008 by Pieter Palmers
3  *
4  * This file is part of FFADO
5  * FFADO = Free Firewire (pro-)audio drivers for linux
6  *
7  * FFADO is based upon FreeBoB
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 2 of the License, or
12  * (at your option) version 3 of the License.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23
24 #include "config.h"
25
26 #include "CycleTimerHelper.h"
27 #include "ieee1394service.h"
28 #include "libutil/PosixThread.h"
29 #include "libutil/Atomic.h"
30 #include "libutil/Watchdog.h"
31
32 #define DLL_PI        (3.141592653589793238)
33 #define DLL_SQRT2     (1.414213562373095049)
34
35 // the high-bandwidth coefficients are used
36 // to speed up inital tracking
37 #define DLL_BANDWIDTH_HIGH (0.2)
38 #define DLL_OMEGA_HIGH     (2.0*DLL_PI*DLL_BANDWIDTH_HIGH)
39 #define DLL_COEFF_B_HIGH   (DLL_SQRT2 * DLL_OMEGA_HIGH)
40 #define DLL_COEFF_C_HIGH   (DLL_OMEGA_HIGH * DLL_OMEGA_HIGH)
41
42 // the low-bandwidth coefficients are used once we have a
43 // good estimate of the internal parameters
44 #define DLL_BANDWIDTH (0.01)
45 #define DLL_OMEGA     (2.0*DLL_PI*DLL_BANDWIDTH)
46 #define DLL_COEFF_B   (DLL_SQRT2 * DLL_OMEGA)
47 #define DLL_COEFF_C   (DLL_OMEGA * DLL_OMEGA)
48
49 // is 5 sec
50 #define UPDATES_WITH_HIGH_BANDWIDTH \
51          (5000000 / IEEE1394SERVICE_CYCLETIMER_DLL_UPDATE_INTERVAL_USEC)
52
53 IMPL_DEBUG_MODULE( CycleTimerHelper, CycleTimerHelper, DEBUG_LEVEL_NORMAL );
54
55 CycleTimerHelper::CycleTimerHelper(Ieee1394Service &parent, unsigned int update_period_us)
56     : m_Parent ( parent )
57     , m_ticks_per_update ( ((uint64_t)TICKS_PER_SECOND) * ((uint64_t)update_period_us) / 1000000ULL )
58     , m_usecs_per_update ( update_period_us )
59     , m_avg_wakeup_delay ( 0.0 )
60     , m_dll_e2 ( 0.0 )
61     , m_current_time_usecs ( 0 )
62     , m_next_time_usecs ( 0 )
63     , m_current_time_ticks ( 0 )
64     , m_next_time_ticks ( 0 )
65     , m_first_run ( true )
66     , m_sleep_until ( 0 )
67     , m_cycle_timer_prev ( 0 )
68     , m_cycle_timer_ticks_prev ( 0 )
69     , m_high_bw_updates ( UPDATES_WITH_HIGH_BANDWIDTH )
70     , m_current_shadow_idx ( 0 )
71     , m_Thread ( NULL )
72     , m_realtime ( false )
73     , m_priority ( 0 )
74     , m_busreset_functor ( NULL)
75     , m_unhandled_busreset ( false )
76 {
77     debugOutput( DEBUG_LEVEL_VERBOSE, "Create %p...\n", this);
78     pthread_mutex_init(&mb_update_lock, NULL);
79 }
80
81 CycleTimerHelper::CycleTimerHelper(Ieee1394Service &parent, unsigned int update_period_us, bool rt, int prio)
82     : m_Parent ( parent )
83     , m_ticks_per_update ( ((uint64_t)TICKS_PER_SECOND) * ((uint64_t)update_period_us) / 1000000ULL )
84     , m_usecs_per_update ( update_period_us )
85     , m_avg_wakeup_delay ( 0.0 )
86     , m_dll_e2 ( 0.0 )
87     , m_current_time_usecs ( 0 )
88     , m_next_time_usecs ( 0 )
89     , m_current_time_ticks ( 0 )
90     , m_next_time_ticks ( 0 )
91     , m_first_run ( true )
92     , m_sleep_until ( 0 )
93     , m_cycle_timer_prev ( 0 )
94     , m_cycle_timer_ticks_prev ( 0 )
95     , m_high_bw_updates ( UPDATES_WITH_HIGH_BANDWIDTH )
96     , m_current_shadow_idx ( 0 )
97     , m_Thread ( NULL )
98     , m_realtime ( rt )
99     , m_priority ( prio )
100     , m_busreset_functor ( NULL)
101     , m_unhandled_busreset ( false )
102 {
103     debugOutput( DEBUG_LEVEL_VERBOSE, "Create %p...\n", this);
104     pthread_mutex_init(&mb_update_lock, NULL);
105 }
106
107 CycleTimerHelper::~CycleTimerHelper()
108 {
109     if (m_Thread) {
110         m_Thread->Stop();
111         delete m_Thread;
112     }
113
114     // unregister the bus reset handler
115     if(m_busreset_functor) {
116         m_Parent.remBusResetHandler( m_busreset_functor );
117         delete m_busreset_functor;
118     }
119     pthread_mutex_destroy(&mb_update_lock);
120 }
121
122 bool
123 CycleTimerHelper::Start()
124 {
125     debugOutput( DEBUG_LEVEL_VERBOSE, "Start %p...\n", this);
126
127     if(!initValues()) {
128         debugFatal("(%p) Could not init values\n", this);
129         return false;
130     }
131
132     m_Thread = new Util::PosixThread(this, m_realtime, m_priority,
133                                      PTHREAD_CANCEL_DEFERRED);
134     if(!m_Thread) {
135         debugFatal("No thread\n");
136         return false;
137     }
138     // register the thread with the RT watchdog
139     Util::Watchdog *watchdog = m_Parent.getWatchdog();
140     if(watchdog) {
141         if(!watchdog->registerThread(m_Thread)) {
142             debugWarning("could not register update thread with watchdog\n");
143         }
144     } else {
145         debugWarning("could not find valid watchdog\n");
146     }
147    
148     if (m_Thread->Start() != 0) {
149         debugFatal("Could not start update thread\n");
150         return false;
151     }
152     return true;
153 }
154
155 bool
156 CycleTimerHelper::initValues()
157 {
158     debugOutput( DEBUG_LEVEL_VERBOSE, "Init values...\n" );
159     pthread_mutex_lock(&mb_update_lock);
160     // initialize the 'prev ctr' values
161     uint64_t local_time;
162     int maxtries2 = 10;
163     do {
164         debugOutput( DEBUG_LEVEL_VERBOSE, "Read CTR...\n" );
165         if(!m_Parent.readCycleTimerReg(&m_cycle_timer_prev, &local_time)) {
166             debugError("Could not read cycle timer register\n");
167             pthread_mutex_unlock(&mb_update_lock);
168             return false;
169         }
170         if (m_cycle_timer_prev == 0) {
171             debugOutput(DEBUG_LEVEL_VERBOSE,
172                         "Bogus CTR: %08X on try %02d\n",
173                         m_cycle_timer_prev, maxtries2);
174         }
175         debugOutput( DEBUG_LEVEL_VERBOSE, " read : CTR: %11lu, local: %17llu\n",
176                             m_cycle_timer_prev, local_time);
177         debugOutput(DEBUG_LEVEL_VERBOSE,
178                            "  ctr   : 0x%08X %11llu (%03us %04ucy %04uticks)\n",
179                            (uint32_t)m_cycle_timer_prev, (uint64_t)CYCLE_TIMER_TO_TICKS(m_cycle_timer_prev),
180                            (unsigned int)CYCLE_TIMER_GET_SECS( m_cycle_timer_prev ),
181                            (unsigned int)CYCLE_TIMER_GET_CYCLES( m_cycle_timer_prev ),
182                            (unsigned int)CYCLE_TIMER_GET_OFFSET( m_cycle_timer_prev ) );
183        
184     } while (m_cycle_timer_prev == 0 && maxtries2--);
185     m_cycle_timer_ticks_prev = CYCLE_TIMER_TO_TICKS(m_cycle_timer_prev);
186
187 #if IEEE1394SERVICE_USE_CYCLETIMER_DLL
188     debugOutput( DEBUG_LEVEL_VERBOSE, "requesting DLL re-init...\n" );
189     m_TimeSource.SleepUsecRelative(1000); // some time to settle
190     m_high_bw_updates = UPDATES_WITH_HIGH_BANDWIDTH;
191     if(!initDLL()) {
192         debugError("(%p) Could not init DLL\n", this);
193         pthread_mutex_unlock(&mb_update_lock);
194         return false;
195     }
196     // make the DLL re-init itself as if it were started up
197     m_first_run = true;
198 #endif
199     pthread_mutex_unlock(&mb_update_lock);
200     debugOutput( DEBUG_LEVEL_VERBOSE, "ready...\n" );
201     return true;
202 }
203
204 bool
205 CycleTimerHelper::Init()
206 {
207     debugOutput( DEBUG_LEVEL_VERBOSE, "Initialize %p...\n", this);
208
209     // register a bus reset handler
210     Util::Functor* m_busreset_functor = new Util::MemberFunctor0< CycleTimerHelper*,
211                 void (CycleTimerHelper::*)() >
212                 ( this, &CycleTimerHelper::busresetHandler, false );
213     if ( !m_busreset_functor ) {
214         debugFatal( "(%p) Could not create busreset handler\n", this );
215         return false;
216     }
217     m_Parent.addBusResetHandler( m_busreset_functor );
218
219     return true;
220 }
221
222 void
223 CycleTimerHelper::busresetHandler()
224 {
225     debugOutput( DEBUG_LEVEL_VERBOSE, "Bus reset...\n" );
226     m_unhandled_busreset = true;
227     // whenever a bus reset occurs, the root node can change,
228     // and the CTR timer can be reset. We should hence reinit
229     // the DLL
230     if(!initValues()) {
231         debugError("(%p) Could not re-init values\n", this);
232     }
233     m_unhandled_busreset = false;
234 }
235
236 bool
237 CycleTimerHelper::setThreadParameters(bool rt, int priority) {
238     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) switch to: (rt=%d, prio=%d)...\n", this, rt, priority);
239     if (priority > THREAD_MAX_RTPRIO) priority = THREAD_MAX_RTPRIO; // cap the priority
240     m_realtime = rt;
241     m_priority = priority;
242
243 #if IEEE1394SERVICE_USE_CYCLETIMER_DLL
244     if (m_Thread) {
245         if (m_realtime) {
246             m_Thread->AcquireRealTime(m_priority);
247         } else {
248             m_Thread->DropRealTime();
249         }
250     }
251 #endif
252
253     return true;
254 }
255
256 #if IEEE1394SERVICE_USE_CYCLETIMER_DLL
257 float
258 CycleTimerHelper::getRate()
259 {
260     float rate = (float)(diffTicks((uint64_t)m_next_time_ticks, (uint64_t)m_current_time_ticks));
261     rate /= (float)(m_next_time_usecs - m_current_time_usecs);
262     return rate;
263 }
264
265 float
266 CycleTimerHelper::getNominalRate()
267 {
268     float rate = ((double)TICKS_PER_SECOND) / 1000000.0;
269     return rate;
270 }
271
272 bool
273 CycleTimerHelper::initDLL() {
274     uint32_t cycle_timer;
275     uint64_t local_time;
276     uint64_t cycle_timer_ticks;
277
278     if(!readCycleTimerWithRetry(&cycle_timer, &local_time, 10)) {
279         debugError("Could not read cycle timer register\n");
280         pthread_mutex_unlock(&mb_update_lock);
281         return false;
282     }
283     cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer);
284
285     debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, " read : CTR: %11lu, local: %17llu\n",
286                         cycle_timer, local_time);
287     debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
288                        "  ctr   : 0x%08X %11llu (%03us %04ucy %04uticks)\n",
289                        (uint32_t)cycle_timer, (uint64_t)cycle_timer_ticks,
290                        (unsigned int)TICKS_TO_SECS( (uint64_t)cycle_timer_ticks ),
291                        (unsigned int)TICKS_TO_CYCLES( (uint64_t)cycle_timer_ticks ),
292                        (unsigned int)TICKS_TO_OFFSET( (uint64_t)cycle_timer_ticks ) );
293
294     m_sleep_until = local_time + m_usecs_per_update;
295     m_dll_e2 = m_ticks_per_update;
296     m_current_time_usecs = local_time;
297     m_next_time_usecs = m_current_time_usecs + m_usecs_per_update;
298     m_current_time_ticks = CYCLE_TIMER_TO_TICKS( cycle_timer );
299     m_next_time_ticks = addTicks( (uint64_t)m_current_time_ticks, (uint64_t)m_dll_e2);
300     debugOutput(DEBUG_LEVEL_VERBOSE, " First run\n");
301     debugOutput(DEBUG_LEVEL_VERBOSE,
302                 "  usecs/update: %lu, ticks/update: %lu, m_dll_e2: %f\n",
303                 m_usecs_per_update, m_ticks_per_update, m_dll_e2);
304     debugOutput(DEBUG_LEVEL_VERBOSE,
305                 "  usecs current: %f, next: %f\n",
306                 m_current_time_usecs, m_next_time_usecs);
307     debugOutput(DEBUG_LEVEL_VERBOSE,
308                 "  ticks current: %f, next: %f\n",
309                 m_current_time_ticks, m_next_time_ticks);
310     return true;
311 }
312
313 bool
314 CycleTimerHelper::Execute()
315 {
316     debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, "Execute %p...\n", this);
317
318     if (!m_first_run) {
319         // wait for the next update period
320         #if DEBUG_EXTREME_ENABLE
321         ffado_microsecs_t now = m_TimeSource.getCurrentTimeAsUsecs();
322         int sleep_time = m_sleep_until - now;
323         debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, "(%p) Sleep until %lld/%f (now: %lld, diff=%d) ...\n",
324                     this, m_sleep_until, m_next_time_usecs, now, sleep_time);
325         #endif
326         m_TimeSource.SleepUsecAbsolute(m_sleep_until);
327         debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, " (%p) back...\n", this);
328     }
329
330     // grab the lock after sleeping, otherwise we can't be interrupted by
331     // the busreset thread (lower prio)
332     pthread_mutex_lock(&mb_update_lock);
333
334     uint32_t cycle_timer;
335     uint64_t local_time;
336     int64_t usecs_late;
337     int ntries=2;
338     uint64_t cycle_timer_ticks;
339     double diff_ticks;
340
341     // if the difference between the predicted value and the
342     // actual value seems to be too large, retry reading the cycle timer
343     // some host controllers return bogus values on some reads
344     // (looks like a non-atomic update of the register)
345     do {
346         if(!readCycleTimerWithRetry(&cycle_timer, &local_time, 10)) {
347             debugError("Could not read cycle timer register\n");
348             pthread_mutex_unlock(&mb_update_lock);
349             return false;
350         }
351         usecs_late = local_time - m_sleep_until;
352         cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer);
353         diff_ticks = diffTicks(cycle_timer_ticks, (int64_t)m_next_time_ticks);
354
355         // check for unrealistic CTR reads (NEC controller does that sometimes)
356         if(diff_ticks < -((double)TICKS_PER_HALFCYCLE)) {
357             debugOutput(DEBUG_LEVEL_VERBOSE,
358                         "have to retry CTR read, diff unrealistic: diff: %f, max: %f\n",
359                         diff_ticks, -((double)TICKS_PER_HALFCYCLE));
360         }
361
362     } while( diff_ticks < -((double)TICKS_PER_HALFCYCLE)
363              && --ntries && !m_first_run && !m_unhandled_busreset);
364
365     if(m_unhandled_busreset) {
366         debugOutput(DEBUG_LEVEL_VERBOSE,
367                     "(%p) Skipping DLL update due to unhandled busreset\n", this);
368         m_sleep_until += m_usecs_per_update;
369         pthread_mutex_unlock(&mb_update_lock);
370         // keep the thread running
371         return true;
372     }
373
374     debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, " read : CTR: %11lu, local: %17llu\n",
375                         cycle_timer, local_time);
376     debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
377                        "  ctr   : 0x%08X %11llu (%03us %04ucy %04uticks)\n",
378                        (uint32_t)cycle_timer, (uint64_t)cycle_timer_ticks,
379                        (unsigned int)TICKS_TO_SECS( (uint64_t)cycle_timer_ticks ),
380                        (unsigned int)TICKS_TO_CYCLES( (uint64_t)cycle_timer_ticks ),
381                        (unsigned int)TICKS_TO_OFFSET( (uint64_t)cycle_timer_ticks ) );
382
383     if (m_first_run) {
384         if(!initDLL()) {
385             debugError("(%p) Could not init DLL\n", this);
386             pthread_mutex_unlock(&mb_update_lock);
387             return false;
388         }
389         m_first_run = false;
390     } else {
391         // calculate next sleep time
392         m_sleep_until += m_usecs_per_update;
393
394         // correct for the latency between the wakeup and the actual CTR
395         // read. The only time we can trust is the time returned by the
396         // CTR read kernel call, since that (should be) atomically read
397         // together with the ctr register itself.
398
399         // if we are usecs_late usecs late
400         // the cycle timer has ticked approx ticks_late ticks too much
401         // if we are woken up early (which shouldn't happen according to POSIX)
402         // the cycle timer has ticked approx -ticks_late too little
403         int64_t ticks_late = (usecs_late * TICKS_PER_SECOND) / 1000000LL;
404         // the corrected difference between predicted and actual ctr
405         // i.e. DLL error signal
406         double diff_ticks_corr;
407         if (ticks_late > 0) {
408             diff_ticks_corr = diff_ticks - ticks_late;
409             debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
410                                "diff_ticks_corr=%f, diff_ticks = %f, ticks_late = %lld\n",
411                                diff_ticks_corr, diff_ticks, ticks_late);
412         } else {
413             debugError("Early wakeup, should not happen!\n");
414             // recover
415             diff_ticks_corr = diff_ticks + ticks_late;
416         }
417
418         #ifdef DEBUG
419         // makes no sense if not running realtime
420         if(m_realtime && usecs_late > 200) {
421             debugOutput(DEBUG_LEVEL_VERBOSE, "Rather late wakeup: %lld usecs\n", usecs_late);
422         }
423         #endif
424
425         // update the x-axis values
426         m_current_time_ticks = m_next_time_ticks;
427
428         // decide what coefficients to use
429         double coeff_b, coeff_c;
430         if (m_high_bw_updates > 0) {
431             coeff_b = DLL_COEFF_B_HIGH;
432             coeff_c = DLL_COEFF_C_HIGH;
433             m_high_bw_updates--;
434             if (m_high_bw_updates == 0) {
435                 debugOutput(DEBUG_LEVEL_VERBOSE,
436                             "Switching to low-bandwidth coefficients\n");
437             }
438         } else {
439             coeff_b = DLL_COEFF_B;
440             coeff_c = DLL_COEFF_C;
441         }
442
443         // it should be ok to not do this in tick space
444         // since diff_ticks_corr should not be near wrapping
445         // (otherwise we are out of range. we need a few calls
446         //  w/o wrapping for this to work. That should not be
447         //  an issue as long as the update interval is smaller
448         //  than the wrapping interval.)
449         // and coeff_b < 1, hence tmp is not near wrapping
450
451         double step_ticks = (coeff_b * diff_ticks_corr);
452         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
453                            "diff_ticks_corr=%f, step_ticks=%f\n",
454                            diff_ticks_corr, step_ticks);
455
456         // the same goes for m_dll_e2, which should be approx equal
457         // to the ticks/usec rate (= 24.576) hence also not near
458         // wrapping
459         step_ticks += m_dll_e2;
460         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
461                            "add %f ticks to step_ticks => step_ticks=%f\n",
462                            m_dll_e2, step_ticks);
463
464         // it can't be that we have to update to a value in the past
465         if(step_ticks < 0) {
466             debugError("negative step: %f! (correcting to nominal)\n", step_ticks);
467             // recover to an estimated value
468             step_ticks = (double)m_ticks_per_update;
469         }
470
471         if(step_ticks > TICKS_PER_SECOND) {
472             debugWarning("rather large step: %f ticks (> 1sec)\n", step_ticks);
473         }
474
475         // now add the step ticks with wrapping.
476         m_next_time_ticks = (double)(addTicks((uint64_t)m_current_time_ticks, (uint64_t)step_ticks));
477
478         // update the DLL state
479         m_dll_e2 += coeff_c * diff_ticks_corr;
480
481         // update the y-axis values
482         m_current_time_usecs = m_next_time_usecs;
483         m_next_time_usecs += m_usecs_per_update;
484
485         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
486                            " usecs: current: %f next: %f usecs_late=%lld ticks_late=%lld\n",
487                            m_current_time_usecs, m_next_time_usecs, usecs_late, ticks_late);
488         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
489                            " ticks: current: %f next: %f diff=%f\n",
490                            m_current_time_ticks, m_next_time_ticks, diff_ticks);
491         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
492                            " ticks: current: %011llu (%03us %04ucy %04uticks)\n",
493                            (uint64_t)m_current_time_ticks,
494                            (unsigned int)TICKS_TO_SECS( (uint64_t)m_current_time_ticks ),
495                            (unsigned int)TICKS_TO_CYCLES( (uint64_t)m_current_time_ticks ),
496                            (unsigned int)TICKS_TO_OFFSET( (uint64_t)m_current_time_ticks ) );
497         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
498                            " ticks: next   : %011llu (%03us %04ucy %04uticks)\n",
499                            (uint64_t)m_next_time_ticks,
500                            (unsigned int)TICKS_TO_SECS( (uint64_t)m_next_time_ticks ),
501                            (unsigned int)TICKS_TO_CYCLES( (uint64_t)m_next_time_ticks ),
502                            (unsigned int)TICKS_TO_OFFSET( (uint64_t)m_next_time_ticks ) );
503
504         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
505                            " state: local: %11llu, dll_e2: %f, rate: %f\n",
506                            local_time, m_dll_e2, getRate());
507     }
508
509     // prepare the new compute vars
510     struct compute_vars new_vars;
511     new_vars.ticks = (uint64_t)(m_current_time_ticks);
512     new_vars.usecs = (uint64_t)m_current_time_usecs;
513     new_vars.rate = getRate();
514
515     // get the next index
516     unsigned int next_idx = (m_current_shadow_idx + 1) % CTRHELPER_NB_SHADOW_VARS;
517
518     // update the next index position
519     m_shadow_vars[next_idx] = new_vars;
520
521     // then we can update the current index
522     m_current_shadow_idx = next_idx;
523
524     pthread_mutex_unlock(&mb_update_lock);
525     return true;
526 }
527
528 uint32_t
529 CycleTimerHelper::getCycleTimerTicks()
530 {
531     uint64_t now = m_Parent.getCurrentTimeAsUsecs();
532     return getCycleTimerTicks(now);
533 }
534
535 uint32_t
536 CycleTimerHelper::getCycleTimerTicks(uint64_t now)
537 {
538     uint32_t retval;
539     struct compute_vars my_vars;
540
541     // get pointer and copy the contents
542     // no locking should be needed since we have more than one
543     // of these vars available, and the copy will always be finished before
544     // m_current_shadow_idx changes since this thread's priority should
545     // be higher than the one of the writer thread. Even if not, we only have to ensure
546     // that the used dataset is consistent. We can use an older dataset if it's consistent
547     // since it will also provide a fairly decent extrapolation.
548     unsigned int curr_idx = m_current_shadow_idx; // NOTE: this needs ordering
549     my_vars = m_shadow_vars[curr_idx];
550
551     int64_t time_diff = now - my_vars.usecs;
552     double y_step_in_ticks = ((double)time_diff) * my_vars.rate;
553     int64_t y_step_in_ticks_int = (int64_t)y_step_in_ticks;
554     uint64_t offset_in_ticks_int = my_vars.ticks;
555
556     if (y_step_in_ticks_int > 0) {
557         retval = addTicks(offset_in_ticks_int, y_step_in_ticks_int);
558 /*        debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "y_step_in_ticks_int > 0: %lld, time_diff: %f, rate: %f, retval: %lu\n",
559                     y_step_in_ticks_int, time_diff, my_vars.rate, retval);*/
560     } else {
561         retval = substractTicks(offset_in_ticks_int, -y_step_in_ticks_int);
562
563         // this can happen if the update thread was woken up earlier than it should have been
564 /*        debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "y_step_in_ticks_int <= 0: %lld, time_diff: %f, rate: %f, retval: %lu\n",
565                     y_step_in_ticks_int, time_diff, my_vars.rate, retval);*/
566     }
567
568     return retval;
569 }
570
571 uint32_t
572 CycleTimerHelper::getCycleTimer()
573 {
574     uint64_t now = m_Parent.getCurrentTimeAsUsecs();
575     return getCycleTimer(now);
576 }
577
578 uint32_t
579 CycleTimerHelper::getCycleTimer(uint64_t now)
580 {
581     uint32_t ticks = getCycleTimerTicks(now);
582     uint32_t result = TICKS_TO_CYCLE_TIMER(ticks);
583 #ifdef DEBUG
584     if(CYCLE_TIMER_TO_TICKS(result) != ticks) {
585         debugWarning("Bad ctr conversion");
586     }
587 #endif
588     return result;
589 }
590
591 #else
592
593 float
594 CycleTimerHelper::getRate()
595 {
596     return getNominalRate();
597 }
598
599 float
600 CycleTimerHelper::getNominalRate()
601 {
602     float rate = ((double)TICKS_PER_SECOND) / 1000000.0;
603     return rate;
604 }
605
606 bool
607 CycleTimerHelper::Execute()
608 {
609     usleep(1000*1000);
610     return true;
611 }
612
613 uint32_t
614 CycleTimerHelper::getCycleTimerTicks()
615 {
616     return CYCLE_TIMER_TO_TICKS(getCycleTimer());
617 }
618
619 uint32_t
620 CycleTimerHelper::getCycleTimerTicks(uint64_t now)
621 {
622     debugWarning("not implemented!\n");
623     return getCycleTimerTicks();
624 }
625
626 uint32_t
627 CycleTimerHelper::getCycleTimer()
628 {
629     uint32_t cycle_timer;
630     uint64_t local_time;
631     readCycleTimerWithRetry(&cycle_timer, &local_time, 10);
632     return cycle_timer;
633 }
634
635 uint32_t
636 CycleTimerHelper::getCycleTimer(uint64_t now)
637 {
638     debugWarning("not implemented!\n");
639     return getCycleTimer();
640 }
641
642 #endif
643
644 bool
645 CycleTimerHelper::readCycleTimerWithRetry(uint32_t *cycle_timer, uint64_t *local_time, int ntries)
646 {
647     bool good=false;
648     int maxtries = ntries;
649
650     do {
651         // the ctr read can return 0 sometimes. if that happens, reread the ctr.
652         int maxtries2=ntries;
653         do {
654             if(!m_Parent.readCycleTimerReg(cycle_timer, local_time)) {
655                 debugError("Could not read cycle timer register\n");
656                 return false;
657             }
658             if (*cycle_timer == 0) {
659                 debugOutput(DEBUG_LEVEL_VERBOSE,
660                            "Bogus CTR: %08X on try %02d\n",
661                            *cycle_timer, maxtries2);
662             }
663         } while (*cycle_timer == 0 && maxtries2--);
664        
665         // catch bogus ctr reads (can happen)
666         uint64_t cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(*cycle_timer);
667    
668         if (diffTicks(cycle_timer_ticks, m_cycle_timer_ticks_prev) < 0) {
669             debugOutput( DEBUG_LEVEL_VERY_VERBOSE,
670                         "non-monotonic CTR (try %02d): %llu -> %llu\n",
671                         maxtries, m_cycle_timer_ticks_prev, cycle_timer_ticks);
672             debugOutput( DEBUG_LEVEL_VERY_VERBOSE,
673                         "                            : %08X -> %08X\n",
674                         m_cycle_timer_prev, *cycle_timer);
675             debugOutput( DEBUG_LEVEL_VERY_VERBOSE,
676                         " current: %011llu (%03us %04ucy %04uticks)\n",
677                         cycle_timer_ticks,
678                         (unsigned int)TICKS_TO_SECS( cycle_timer_ticks ),
679                         (unsigned int)TICKS_TO_CYCLES( cycle_timer_ticks ),
680                         (unsigned int)TICKS_TO_OFFSET( cycle_timer_ticks ) );
681             debugOutput( DEBUG_LEVEL_VERY_VERBOSE,
682                         " prev   : %011llu (%03us %04ucy %04uticks)\n",
683                         m_cycle_timer_ticks_prev,
684                         (unsigned int)TICKS_TO_SECS( m_cycle_timer_ticks_prev ),
685                         (unsigned int)TICKS_TO_CYCLES( m_cycle_timer_ticks_prev ),
686                         (unsigned int)TICKS_TO_OFFSET( m_cycle_timer_ticks_prev ) );
687         } else {
688             good = true;
689             m_cycle_timer_prev = *cycle_timer;
690             m_cycle_timer_ticks_prev = cycle_timer_ticks;
691         }
692     } while (!good && maxtries--);
693     return true;
694 }
695
696 void
697 CycleTimerHelper::setVerboseLevel(int l)
698 {
699     setDebugLevel(l);
700 }
Note: See TracBrowser for help on using the browser.