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

Revision 919, 20.2 kB (checked in by ppalmers, 16 years ago)

fix concurrency issue in cycle timer updater

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
31 #define DLL_PI        (3.141592653589793238)
32 #define DLL_SQRT2     (1.414213562373095049)
33
34 // the high-bandwidth coefficients are used
35 // to speed up inital tracking
36 #define DLL_BANDWIDTH_HIGH (0.2)
37 #define DLL_OMEGA_HIGH     (2.0*DLL_PI*DLL_BANDWIDTH_HIGH)
38 #define DLL_COEFF_B_HIGH   (DLL_SQRT2 * DLL_OMEGA_HIGH)
39 #define DLL_COEFF_C_HIGH   (DLL_OMEGA_HIGH * DLL_OMEGA_HIGH)
40
41 // the low-bandwidth coefficients are used once we have a
42 // good estimate of the internal parameters
43 #define DLL_BANDWIDTH (0.01)
44 #define DLL_OMEGA     (2.0*DLL_PI*DLL_BANDWIDTH)
45 #define DLL_COEFF_B   (DLL_SQRT2 * DLL_OMEGA)
46 #define DLL_COEFF_C   (DLL_OMEGA * DLL_OMEGA)
47
48 // is 5 sec
49 #define UPDATES_WITH_HIGH_BANDWIDTH \
50          (5000000 / IEEE1394SERVICE_CYCLETIMER_DLL_UPDATE_INTERVAL_USEC)
51
52 IMPL_DEBUG_MODULE( CycleTimerHelper, CycleTimerHelper, DEBUG_LEVEL_NORMAL );
53
54 CycleTimerHelper::CycleTimerHelper(Ieee1394Service &parent, unsigned int update_period_us)
55     : m_Parent ( parent )
56     , m_ticks_per_update ( ((uint64_t)TICKS_PER_SECOND) * ((uint64_t)update_period_us) / 1000000ULL )
57     , m_usecs_per_update ( update_period_us )
58     , m_avg_wakeup_delay ( 0.0 )
59     , m_dll_e2 ( 0.0 )
60     , m_current_time_usecs ( 0 )
61     , m_next_time_usecs ( 0 )
62     , m_current_time_ticks ( 0 )
63     , m_next_time_ticks ( 0 )
64     , m_first_run ( true )
65     , m_sleep_until ( 0 )
66     , m_cycle_timer_prev ( 0 )
67     , m_cycle_timer_ticks_prev ( 0 )
68     , m_high_bw_updates ( UPDATES_WITH_HIGH_BANDWIDTH )
69     , m_current_shadow_idx ( 0 )
70     , m_Thread ( NULL )
71     , m_realtime ( false )
72     , m_priority ( 0 )
73 {
74     debugOutput( DEBUG_LEVEL_VERBOSE, "Create %p...\n", this);
75 }
76
77 CycleTimerHelper::CycleTimerHelper(Ieee1394Service &parent, unsigned int update_period_us, bool rt, int prio)
78     : m_Parent ( parent )
79     , m_ticks_per_update ( ((uint64_t)TICKS_PER_SECOND) * ((uint64_t)update_period_us) / 1000000ULL )
80     , m_usecs_per_update ( update_period_us )
81     , m_avg_wakeup_delay ( 0.0 )
82     , m_dll_e2 ( 0.0 )
83     , m_current_time_usecs ( 0 )
84     , m_next_time_usecs ( 0 )
85     , m_current_time_ticks ( 0 )
86     , m_next_time_ticks ( 0 )
87     , m_first_run ( true )
88     , m_sleep_until ( 0 )
89     , m_cycle_timer_prev ( 0 )
90     , m_cycle_timer_ticks_prev ( 0 )
91     , m_high_bw_updates ( UPDATES_WITH_HIGH_BANDWIDTH )
92     , m_current_shadow_idx ( 0 )
93     , m_Thread ( NULL )
94     , m_realtime ( rt )
95     , m_priority ( prio )
96 {
97     debugOutput( DEBUG_LEVEL_VERBOSE, "Create %p...\n", this);
98 }
99
100 CycleTimerHelper::~CycleTimerHelper()
101 {
102     if (m_Thread) {
103         m_Thread->Stop();
104         delete m_Thread;
105     }
106 }
107
108 bool
109 CycleTimerHelper::Start()
110 {
111     debugOutput( DEBUG_LEVEL_VERBOSE, "Start %p...\n", this);
112
113     // initialize the 'prev ctr' values
114     uint64_t local_time;
115     int maxtries2 = 10;
116     do {
117         if(!m_Parent.readCycleTimerReg(&m_cycle_timer_prev, &local_time)) {
118             debugError("Could not read cycle timer register\n");
119             return false;
120         }
121         if (m_cycle_timer_prev == 0) {
122             debugOutput(DEBUG_LEVEL_VERBOSE,
123                         "Bogus CTR: %08X on try %02d\n",
124                         m_cycle_timer_prev, maxtries2);
125         }
126     } while (m_cycle_timer_prev == 0 && maxtries2--);
127     m_cycle_timer_ticks_prev = CYCLE_TIMER_TO_TICKS(m_cycle_timer_prev);
128
129 #if IEEE1394SERVICE_USE_CYCLETIMER_DLL
130     m_high_bw_updates = UPDATES_WITH_HIGH_BANDWIDTH;
131     m_Thread = new Util::PosixThread(this, m_realtime, m_priority,
132                                      PTHREAD_CANCEL_DEFERRED);
133     if(!m_Thread) {
134         debugFatal("No thread\n");
135         return false;
136     }
137     if (m_Thread->Start() != 0) {
138         debugFatal("Could not start update thread\n");
139         return false;
140     }
141 #endif
142     return true;
143 }
144
145 bool
146 CycleTimerHelper::Init()
147 {
148     debugOutput( DEBUG_LEVEL_VERBOSE, "Initialize %p...\n", this);
149     return true;
150 }
151
152 bool
153 CycleTimerHelper::setThreadParameters(bool rt, int priority) {
154     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) switch to: (rt=%d, prio=%d)...\n", this, rt, priority);
155     if (priority > THREAD_MAX_RTPRIO) priority = THREAD_MAX_RTPRIO; // cap the priority
156     m_realtime = rt;
157     m_priority = priority;
158
159 #if IEEE1394SERVICE_USE_CYCLETIMER_DLL
160     if (m_Thread) {
161         if (m_realtime) {
162             m_Thread->AcquireRealTime(m_priority);
163         } else {
164             m_Thread->DropRealTime();
165         }
166     }
167 #endif
168
169     return true;
170 }
171
172 #if IEEE1394SERVICE_USE_CYCLETIMER_DLL
173 float
174 CycleTimerHelper::getRate()
175 {
176     float rate = (float)(diffTicks((uint64_t)m_next_time_ticks, (uint64_t)m_current_time_ticks));
177     rate /= (float)(m_next_time_usecs - m_current_time_usecs);
178     return rate;
179 }
180
181 float
182 CycleTimerHelper::getNominalRate()
183 {
184     float rate = ((double)TICKS_PER_SECOND) / 1000000.0;
185     return rate;
186 }
187
188 bool
189 CycleTimerHelper::Execute()
190 {
191     debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, "Execute %p...\n", this);
192     if (!m_first_run) {
193         // wait for the next update period
194         ffado_microsecs_t now = m_TimeSource.getCurrentTimeAsUsecs();
195         int sleep_time = m_sleep_until - now;
196         debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, "(%p) Sleep until %lld/%f (now: %lld, diff=%d) ...\n",
197                     this, m_sleep_until, m_next_time_usecs, now, sleep_time);
198         m_TimeSource.SleepUsecAbsolute(m_sleep_until);
199         debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, " (%p) back...\n", this);
200     }
201
202     uint32_t cycle_timer;
203     uint64_t local_time;
204     int64_t usecs_late;
205     int ntries=2;
206     uint64_t cycle_timer_ticks;
207     double diff_ticks;
208
209     // if the difference between the predicted value and the
210     // actual value seems to be too large, retry reading the cycle timer
211     // some host controllers return bogus values on some reads
212     // (looks like a non-atomic update of the register)
213     do {
214         if(!readCycleTimerWithRetry(&cycle_timer, &local_time, 10)) {
215             debugError("Could not read cycle timer register\n");
216             return false;
217         }
218         usecs_late = local_time - m_sleep_until;
219         cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer);
220         diff_ticks = diffTicks(cycle_timer_ticks, (int64_t)m_next_time_ticks);
221
222         // check for unrealistic CTR reads (NEC controller does that sometimes)
223         if(diff_ticks < -((double)TICKS_PER_HALFCYCLE)) {
224             debugOutput(DEBUG_LEVEL_VERBOSE,
225                         "have to retry CTR read, diff unrealistic: diff: %f, max: %f\n",
226                         diff_ticks, -((double)TICKS_PER_HALFCYCLE));
227         }
228
229     } while(diff_ticks < -((double)TICKS_PER_HALFCYCLE) && --ntries && !m_first_run);
230
231     debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, " read : CTR: %11lu, local: %17llu\n",
232                         cycle_timer, local_time);
233         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
234                            "  ctr   : 0x%08X %11llu (%03us %04ucy %04uticks)\n",
235                            (uint32_t)cycle_timer, (uint64_t)cycle_timer_ticks,
236                            (unsigned int)TICKS_TO_SECS( (uint64_t)cycle_timer_ticks ),
237                            (unsigned int)TICKS_TO_CYCLES( (uint64_t)cycle_timer_ticks ),
238                            (unsigned int)TICKS_TO_OFFSET( (uint64_t)cycle_timer_ticks ) );
239
240     if (m_first_run) {
241         m_sleep_until = local_time + m_usecs_per_update;
242         m_dll_e2 = m_ticks_per_update;
243         m_current_time_usecs = local_time;
244         m_next_time_usecs = m_current_time_usecs + m_usecs_per_update;
245         m_current_time_ticks = CYCLE_TIMER_TO_TICKS( cycle_timer );
246         m_next_time_ticks = addTicks( (uint64_t)m_current_time_ticks, (uint64_t)m_dll_e2);
247         debugOutput(DEBUG_LEVEL_VERBOSE, " First run\n");
248         debugOutput(DEBUG_LEVEL_VERBOSE,
249                     "  usecs/update: %lu, ticks/update: %lu, m_dll_e2: %f\n",
250                     m_usecs_per_update, m_ticks_per_update, m_dll_e2);
251         debugOutput(DEBUG_LEVEL_VERBOSE,
252                     "  usecs current: %f, next: %f\n",
253                     m_current_time_usecs, m_next_time_usecs);
254         debugOutput(DEBUG_LEVEL_VERBOSE,
255                     "  ticks current: %f, next: %f\n",
256                     m_current_time_ticks, m_next_time_ticks);
257         m_first_run = false;
258     } else {
259         // calculate next sleep time
260         m_sleep_until += m_usecs_per_update;
261
262         // correct for the latency between the wakeup and the actual CTR
263         // read. The only time we can trust is the time returned by the
264         // CTR read kernel call, since that (should be) atomically read
265         // together with the ctr register itself.
266
267         // if we are usecs_late usecs late
268         // the cycle timer has ticked approx ticks_late ticks too much
269         // if we are woken up early (which shouldn't happen according to POSIX)
270         // the cycle timer has ticked approx -ticks_late too little
271         int64_t ticks_late = (usecs_late * TICKS_PER_SECOND) / 1000000LL;
272         // the corrected difference between predicted and actual ctr
273         // i.e. DLL error signal
274         double diff_ticks_corr;
275         if (ticks_late > 0) {
276             diff_ticks_corr = diff_ticks - ticks_late;
277             debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
278                                "diff_ticks_corr=%f, diff_ticks = %f, ticks_late = %lld\n",
279                                diff_ticks_corr, diff_ticks, ticks_late);
280         } else {
281             debugError("Early wakeup, should not happen!\n");
282             // recover
283             diff_ticks_corr = diff_ticks + ticks_late;
284         }
285
286         #ifdef DEBUG
287         // makes no sense if not running realtime
288         if(m_realtime && usecs_late > 200) {
289             debugOutput(DEBUG_LEVEL_VERBOSE, "Rather late wakeup: %lld usecs\n", usecs_late);
290         }
291         #endif
292
293         // update the x-axis values
294         m_current_time_ticks = m_next_time_ticks;
295
296         // decide what coefficients to use
297         double coeff_b, coeff_c;
298         if (m_high_bw_updates > 0) {
299             coeff_b = DLL_COEFF_B_HIGH;
300             coeff_c = DLL_COEFF_C_HIGH;
301             m_high_bw_updates--;
302             if (m_high_bw_updates == 0) {
303                 debugOutput(DEBUG_LEVEL_VERBOSE,
304                             "Switching to low-bandwidth coefficients\n");
305             }
306         } else {
307             coeff_b = DLL_COEFF_B;
308             coeff_c = DLL_COEFF_C;
309         }
310
311         // it should be ok to not do this in tick space
312         // since diff_ticks_corr should not be near wrapping
313         // (otherwise we are out of range. we need a few calls
314         //  w/o wrapping for this to work. That should not be
315         //  an issue as long as the update interval is smaller
316         //  than the wrapping interval.)
317         // and coeff_b < 1, hence tmp is not near wrapping
318
319         double step_ticks = (coeff_b * diff_ticks_corr);
320         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
321                            "diff_ticks_corr=%f, step_ticks=%f\n",
322                            diff_ticks_corr, step_ticks);
323
324         // the same goes for m_dll_e2, which should be approx equal
325         // to the ticks/usec rate (= 24.576) hence also not near
326         // wrapping
327         step_ticks += m_dll_e2;
328         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
329                            "add %f ticks to step_ticks => step_ticks=%f\n",
330                            m_dll_e2, step_ticks);
331
332         // it can't be that we have to update to a value in the past
333         if(step_ticks < 0) {
334             debugError("negative step: %f! (correcting to nominal)\n", step_ticks);
335             // recover to an estimated value
336             step_ticks = (double)m_ticks_per_update;
337         }
338
339         if(step_ticks > TICKS_PER_SECOND) {
340             debugWarning("rather large step: %f ticks (> 1sec)\n", step_ticks);
341         }
342
343         // now add the step ticks with wrapping.
344         m_next_time_ticks = (double)(addTicks((uint64_t)m_current_time_ticks, (uint64_t)step_ticks));
345
346         // update the DLL state
347         m_dll_e2 += coeff_c * diff_ticks_corr;
348
349         // update the y-axis values
350         m_current_time_usecs = m_next_time_usecs;
351         m_next_time_usecs += m_usecs_per_update;
352
353         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
354                            " usecs: current: %f next: %f usecs_late=%lld ticks_late=%lld\n",
355                            m_current_time_usecs, m_next_time_usecs, usecs_late, ticks_late);
356         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
357                            " ticks: current: %f next: %f diff=%f\n",
358                            m_current_time_ticks, m_next_time_ticks, diff_ticks);
359         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
360                            " ticks: current: %011llu (%03us %04ucy %04uticks)\n",
361                            (uint64_t)m_current_time_ticks,
362                            (unsigned int)TICKS_TO_SECS( (uint64_t)m_current_time_ticks ),
363                            (unsigned int)TICKS_TO_CYCLES( (uint64_t)m_current_time_ticks ),
364                            (unsigned int)TICKS_TO_OFFSET( (uint64_t)m_current_time_ticks ) );
365         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
366                            " ticks: next   : %011llu (%03us %04ucy %04uticks)\n",
367                            (uint64_t)m_next_time_ticks,
368                            (unsigned int)TICKS_TO_SECS( (uint64_t)m_next_time_ticks ),
369                            (unsigned int)TICKS_TO_CYCLES( (uint64_t)m_next_time_ticks ),
370                            (unsigned int)TICKS_TO_OFFSET( (uint64_t)m_next_time_ticks ) );
371
372         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
373                            " state: local: %11llu, dll_e2: %f, rate: %f\n",
374                            local_time, m_dll_e2, getRate());
375     }
376
377     // prepare the new compute vars
378     struct compute_vars new_vars;
379     new_vars.ticks = (uint64_t)(m_current_time_ticks);
380     new_vars.usecs = (uint64_t)m_current_time_usecs;
381     new_vars.rate = getRate();
382
383     // get the next index
384     unsigned int next_idx = (m_current_shadow_idx + 1) % CTRHELPER_NB_SHADOW_VARS;
385
386     // update the next index position
387     m_shadow_vars[next_idx] = new_vars;
388
389     // then we can update the current index
390     m_current_shadow_idx = next_idx;
391
392     return true;
393 }
394
395 uint32_t
396 CycleTimerHelper::getCycleTimerTicks()
397 {
398     uint64_t now = m_Parent.getCurrentTimeAsUsecs();
399     return getCycleTimerTicks(now);
400 }
401
402 uint32_t
403 CycleTimerHelper::getCycleTimerTicks(uint64_t now)
404 {
405     uint32_t retval;
406     struct compute_vars my_vars;
407
408     // get pointer and copy the contents
409     // no locking should be needed since we have more than one
410     // of these vars available, and the copy will always be finished before
411     // m_current_shadow_idx changes since this thread's priority should
412     // be higher than the one of the writer thread. Even if not, we only have to ensure
413     // that the used dataset is consistent. We can use an older dataset if it's consistent
414     // since it will also provide a fairly decent extrapolation.
415     unsigned int curr_idx = m_current_shadow_idx; // NOTE: this needs ordering
416     my_vars = m_shadow_vars[curr_idx];
417
418     int64_t time_diff = now - my_vars.usecs;
419     double y_step_in_ticks = ((double)time_diff) * my_vars.rate;
420     int64_t y_step_in_ticks_int = (int64_t)y_step_in_ticks;
421     uint64_t offset_in_ticks_int = my_vars.ticks;
422
423     if (y_step_in_ticks_int > 0) {
424         retval = addTicks(offset_in_ticks_int, y_step_in_ticks_int);
425 /*        debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "y_step_in_ticks_int > 0: %lld, time_diff: %f, rate: %f, retval: %lu\n",
426                     y_step_in_ticks_int, time_diff, my_vars.rate, retval);*/
427     } else {
428         retval = substractTicks(offset_in_ticks_int, -y_step_in_ticks_int);
429
430         // this can happen if the update thread was woken up earlier than it should have been
431 /*        debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "y_step_in_ticks_int <= 0: %lld, time_diff: %f, rate: %f, retval: %lu\n",
432                     y_step_in_ticks_int, time_diff, my_vars.rate, retval);*/
433     }
434
435     return retval;
436 }
437
438 uint32_t
439 CycleTimerHelper::getCycleTimer()
440 {
441     uint64_t now = m_Parent.getCurrentTimeAsUsecs();
442     return getCycleTimer(now);
443 }
444
445 uint32_t
446 CycleTimerHelper::getCycleTimer(uint64_t now)
447 {
448     uint32_t ticks = getCycleTimerTicks(now);
449     uint32_t result = TICKS_TO_CYCLE_TIMER(ticks);
450 #ifdef DEBUG
451     if(CYCLE_TIMER_TO_TICKS(result) != ticks) {
452         debugWarning("Bad ctr conversion");
453     }
454 #endif
455     return result;
456 }
457
458 #else
459
460 float
461 CycleTimerHelper::getRate()
462 {
463     return getNominalRate();
464 }
465
466 float
467 CycleTimerHelper::getNominalRate()
468 {
469     float rate = ((double)TICKS_PER_SECOND) / 1000000.0;
470     return rate;
471 }
472
473 bool
474 CycleTimerHelper::Execute()
475 {
476     usleep(1000*1000);
477     return true;
478 }
479
480 uint32_t
481 CycleTimerHelper::getCycleTimerTicks()
482 {
483     return CYCLE_TIMER_TO_TICKS(getCycleTimer());
484 }
485
486 uint32_t
487 CycleTimerHelper::getCycleTimerTicks(uint64_t now)
488 {
489     debugWarning("not implemented!\n");
490     return getCycleTimerTicks();
491 }
492
493 uint32_t
494 CycleTimerHelper::getCycleTimer()
495 {
496     uint32_t cycle_timer;
497     uint64_t local_time;
498     readCycleTimerWithRetry(&cycle_timer, &local_time, 10);
499     return cycle_timer;
500 }
501
502 uint32_t
503 CycleTimerHelper::getCycleTimer(uint64_t now)
504 {
505     debugWarning("not implemented!\n");
506     return getCycleTimer();
507 }
508
509 #endif
510
511 bool
512 CycleTimerHelper::readCycleTimerWithRetry(uint32_t *cycle_timer, uint64_t *local_time, int ntries)
513 {
514     bool good=false;
515     int maxtries = ntries;
516
517     do {
518         // the ctr read can return 0 sometimes. if that happens, reread the ctr.
519         int maxtries2=ntries;
520         do {
521             if(!m_Parent.readCycleTimerReg(cycle_timer, local_time)) {
522                 debugError("Could not read cycle timer register\n");
523                 return false;
524             }
525             if (*cycle_timer == 0) {
526                 debugOutput(DEBUG_LEVEL_VERBOSE,
527                            "Bogus CTR: %08X on try %02d\n",
528                            *cycle_timer, maxtries2);
529             }
530         } while (*cycle_timer == 0 && maxtries2--);
531        
532         // catch bogus ctr reads (can happen)
533         uint64_t cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(*cycle_timer);
534    
535         if (diffTicks(cycle_timer_ticks, m_cycle_timer_ticks_prev) < 0) {
536             debugOutput( DEBUG_LEVEL_VERY_VERBOSE,
537                         "non-monotonic CTR (try %02d): %llu -> %llu\n",
538                         maxtries, m_cycle_timer_ticks_prev, cycle_timer_ticks);
539             debugOutput( DEBUG_LEVEL_VERY_VERBOSE,
540                         "                            : %08X -> %08X\n",
541                         m_cycle_timer_prev, *cycle_timer);
542             debugOutput( DEBUG_LEVEL_VERY_VERBOSE,
543                         " current: %011llu (%03us %04ucy %04uticks)\n",
544                         cycle_timer_ticks,
545                         (unsigned int)TICKS_TO_SECS( cycle_timer_ticks ),
546                         (unsigned int)TICKS_TO_CYCLES( cycle_timer_ticks ),
547                         (unsigned int)TICKS_TO_OFFSET( cycle_timer_ticks ) );
548             debugOutput( DEBUG_LEVEL_VERY_VERBOSE,
549                         " prev   : %011llu (%03us %04ucy %04uticks)\n",
550                         m_cycle_timer_ticks_prev,
551                         (unsigned int)TICKS_TO_SECS( m_cycle_timer_ticks_prev ),
552                         (unsigned int)TICKS_TO_CYCLES( m_cycle_timer_ticks_prev ),
553                         (unsigned int)TICKS_TO_OFFSET( m_cycle_timer_ticks_prev ) );
554         } else {
555             good = true;
556             m_cycle_timer_prev = *cycle_timer;
557             m_cycle_timer_ticks_prev = cycle_timer_ticks;
558         }
559     } while (!good && maxtries--);
560     return true;
561 }
562
563 void
564 CycleTimerHelper::setVerboseLevel(int l)
565 {
566     setDebugLevel(l);
567 }
Note: See TracBrowser for help on using the browser.