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

Revision 904, 16.6 kB (checked in by ppalmers, 16 years ago)

simplify threading. Each port now gets two threads: one for transmit and one for receive.

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
30 #define DLL_PI        (3.141592653589793238)
31 #define DLL_SQRT2     (1.414213562373095049)
32
33 // the high-bandwidth coefficients are used
34 // to speed up inital tracking
35 #define DLL_BANDWIDTH_HIGH (0.1)
36 #define DLL_OMEGA_HIGH     (2.0*DLL_PI*DLL_BANDWIDTH_HIGH)
37 #define DLL_COEFF_B_HIGH   (DLL_SQRT2 * DLL_OMEGA_HIGH)
38 #define DLL_COEFF_C_HIGH   (DLL_OMEGA_HIGH * DLL_OMEGA_HIGH)
39
40 // the low-bandwidth coefficients are used once we have a
41 // good estimate of the internal parameters
42 #define DLL_BANDWIDTH (0.01)
43 #define DLL_OMEGA     (2.0*DLL_PI*DLL_BANDWIDTH)
44 #define DLL_COEFF_B   (DLL_SQRT2 * DLL_OMEGA)
45 #define DLL_COEFF_C   (DLL_OMEGA * DLL_OMEGA)
46
47 #define UPDATES_WITH_HIGH_BANDWIDTH 200
48
49 /*
50 #define ENTER_CRITICAL_SECTION { \
51     if (pthread_mutex_trylock(&m_compute_vars_lock) == EBUSY) { \
52         debugWarning(" (%p) lock clash\n", this); \
53         ENTER_CRITICAL_SECTION; \
54     } \
55     }
56 */
57 #define ENTER_CRITICAL_SECTION { \
58     pthread_mutex_lock(&m_compute_vars_lock); \
59     }
60 #define EXIT_CRITICAL_SECTION { \
61     pthread_mutex_unlock(&m_compute_vars_lock); \
62     }
63
64 IMPL_DEBUG_MODULE( CycleTimerHelper, CycleTimerHelper, DEBUG_LEVEL_NORMAL );
65
66 CycleTimerHelper::CycleTimerHelper(Ieee1394Service &parent, unsigned int update_period_us)
67     : m_Parent ( parent )
68     , m_ticks_per_update ( ((uint64_t)TICKS_PER_SECOND) * ((uint64_t)update_period_us) / 1000000ULL )
69     , m_usecs_per_update ( update_period_us )
70     , m_avg_wakeup_delay ( 0.0 )
71     , m_dll_e2 ( 0.0 )
72     , m_current_time_usecs ( 0 )
73     , m_next_time_usecs ( 0 )
74     , m_current_time_ticks ( 0 )
75     , m_next_time_ticks ( 0 )
76     , m_first_run ( true )
77     , m_sleep_until ( 0 )
78     , m_cycle_timer_prev ( 0 )
79     , m_cycle_timer_ticks_prev ( 0 )
80     , m_high_bw_updates ( UPDATES_WITH_HIGH_BANDWIDTH )
81     , m_Thread ( NULL )
82     , m_realtime ( false )
83     , m_priority ( 0 )
84 {
85     debugOutput( DEBUG_LEVEL_VERBOSE, "Create %p...\n", this);
86 }
87
88 CycleTimerHelper::CycleTimerHelper(Ieee1394Service &parent, unsigned int update_period_us, bool rt, int prio)
89     : m_Parent ( parent )
90     , m_ticks_per_update ( ((uint64_t)TICKS_PER_SECOND) * ((uint64_t)update_period_us) / 1000000ULL )
91     , m_usecs_per_update ( update_period_us )
92     , m_avg_wakeup_delay ( 0.0 )
93     , m_dll_e2 ( 0.0 )
94     , m_current_time_usecs ( 0 )
95     , m_next_time_usecs ( 0 )
96     , m_current_time_ticks ( 0 )
97     , m_next_time_ticks ( 0 )
98     , m_first_run ( true )
99     , m_sleep_until ( 0 )
100     , m_cycle_timer_prev ( 0 )
101     , m_cycle_timer_ticks_prev ( 0 )
102     , m_high_bw_updates ( UPDATES_WITH_HIGH_BANDWIDTH )
103     , m_Thread ( NULL )
104     , m_realtime ( rt )
105     , m_priority ( prio )
106 {
107     debugOutput( DEBUG_LEVEL_VERBOSE, "Create %p...\n", this);
108 }
109
110 CycleTimerHelper::~CycleTimerHelper()
111 {
112     if (m_Thread) {
113         m_Thread->Stop();
114         delete m_Thread;
115     }
116 }
117
118 bool
119 CycleTimerHelper::Start()
120 {
121     debugOutput( DEBUG_LEVEL_VERBOSE, "Start %p...\n", this);
122
123     // initialize the 'prev ctr' values
124     uint64_t local_time;
125     int maxtries2 = 10;
126     do {
127         if(!m_Parent.readCycleTimerReg(&m_cycle_timer_prev, &local_time)) {
128             debugError("Could not read cycle timer register\n");
129             return false;
130         }
131         if (m_cycle_timer_prev == 0) {
132             debugOutput(DEBUG_LEVEL_VERBOSE,
133                         "Bogus CTR: %08X on try %02d\n",
134                         m_cycle_timer_prev, maxtries2);
135         }
136     } while (m_cycle_timer_prev == 0 && maxtries2--);
137     m_cycle_timer_ticks_prev = CYCLE_TIMER_TO_TICKS(m_cycle_timer_prev);
138
139 #if IEEE1394SERVICE_USE_CYCLETIMER_DLL
140     m_high_bw_updates = UPDATES_WITH_HIGH_BANDWIDTH;
141     m_Thread = new Util::PosixThread(this, m_realtime, m_priority,
142                                      PTHREAD_CANCEL_DEFERRED);
143     if(!m_Thread) {
144         debugFatal("No thread\n");
145         return false;
146     }
147     if (m_Thread->Start() != 0) {
148         debugFatal("Could not start update thread\n");
149         return false;
150     }
151 #endif
152     return true;
153 }
154
155 bool
156 CycleTimerHelper::Init()
157 {
158     debugOutput( DEBUG_LEVEL_VERBOSE, "Initialize %p...\n", this);
159     pthread_mutex_init(&m_compute_vars_lock, NULL);
160     return true;
161 }
162
163 bool
164 CycleTimerHelper::setThreadParameters(bool rt, int priority) {
165     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) switch to: (rt=%d, prio=%d)...\n", this, rt, priority);
166     if (priority > THREAD_MAX_RTPRIO) priority = THREAD_MAX_RTPRIO; // cap the priority
167     m_realtime = rt;
168     m_priority = priority;
169
170 #if IEEE1394SERVICE_USE_CYCLETIMER_DLL
171     if (m_Thread) {
172         if (m_realtime) {
173             m_Thread->AcquireRealTime(m_priority);
174         } else {
175             m_Thread->DropRealTime();
176         }
177     }
178 #endif
179
180     return true;
181 }
182
183 #if IEEE1394SERVICE_USE_CYCLETIMER_DLL
184 float
185 CycleTimerHelper::getRate()
186 {
187     float rate = (float)(diffTicks((uint64_t)m_next_time_ticks, (uint64_t)m_current_time_ticks));
188     rate /= (float)(m_next_time_usecs - m_current_time_usecs);
189     return rate;
190 }
191
192 float
193 CycleTimerHelper::getNominalRate()
194 {
195     float rate = ((double)TICKS_PER_SECOND) / 1000000.0;
196     return rate;
197 }
198
199 bool
200 CycleTimerHelper::Execute()
201 {
202     debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, "Execute %p...\n", this);
203     if (!m_first_run) {
204         // wait for the next update period
205         ffado_microsecs_t now = m_TimeSource.getCurrentTimeAsUsecs();
206         int sleep_time = m_sleep_until - now;
207         debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, "(%p) Sleep until %lld/%f (now: %lld, diff=%d) ...\n",
208                     this, m_sleep_until, m_next_time_usecs, now, sleep_time);
209         m_TimeSource.SleepUsecAbsolute(m_sleep_until);
210         debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, " (%p) back...\n", this);
211     }
212
213     uint32_t cycle_timer;
214     uint64_t local_time;
215     int64_t usecs_late;
216     int ntries=2;
217     uint64_t cycle_timer_ticks;
218     double diff_ticks;
219
220     // if the difference between the predicted value and the
221     // actual value seems to be too large, retry reading the cycle timer
222     // some host controllers return bogus values on some reads
223     // (looks like a non-atomic update of the register)
224     do {
225         if(!readCycleTimerWithRetry(&cycle_timer, &local_time, 10)) {
226             debugError("Could not read cycle timer register\n");
227             return false;
228         }
229         usecs_late = local_time - m_sleep_until;
230         cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer);
231         diff_ticks = diffTicks(cycle_timer_ticks, (int64_t)m_next_time_ticks);
232         if(diff_ticks < -((double)TICKS_PER_HALFCYCLE)) {
233             debugOutput(DEBUG_LEVEL_VERBOSE, "have to retry, diff = %f\n",diff_ticks);
234         }
235
236     } while(diff_ticks < -((double)TICKS_PER_HALFCYCLE) && --ntries && !m_first_run);
237
238     debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, " read : CTR: %11lu, local: %17llu\n",
239                         cycle_timer, local_time);
240
241     if (m_first_run) {
242         m_sleep_until = local_time + m_usecs_per_update;
243         m_dll_e2 = m_ticks_per_update;
244         m_current_time_usecs = local_time;
245         m_next_time_usecs = m_current_time_usecs + m_usecs_per_update;
246         m_current_time_ticks = CYCLE_TIMER_TO_TICKS( cycle_timer );
247         m_next_time_ticks = addTicks( (uint64_t)m_current_time_ticks, (uint64_t)m_dll_e2);
248         debugOutput( DEBUG_LEVEL_VERBOSE, " First run\n");
249         debugOutput( DEBUG_LEVEL_VERBOSE, "  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, "  usecs current: %f, next: %f\n", m_current_time_usecs, m_next_time_usecs);
252         debugOutput( DEBUG_LEVEL_VERBOSE, "  ticks current: %f, next: %f\n", m_current_time_ticks, m_next_time_ticks);
253         m_first_run = false;
254     } else {
255         m_sleep_until += m_usecs_per_update;
256
257         double diff_ticks_corr;
258         // correct for late wakeup
259         int64_t ticks_late = (usecs_late * TICKS_PER_SECOND) / 1000000LL;
260         if (ticks_late > 0) {
261             // if we are usecs_late usecs late
262             // the cycle timer has ticked approx ticks_late ticks too much
263             cycle_timer_ticks = substractTicks(cycle_timer_ticks, ticks_late);
264             diff_ticks_corr = diff_ticks - ticks_late;
265         } else {
266             debugError("Early wakeup, should not happen!\n");
267             // recover
268             cycle_timer_ticks = addTicks(cycle_timer_ticks, -ticks_late);
269             diff_ticks_corr = diff_ticks + ticks_late;
270         }
271
272         #ifdef DEBUG
273         if(usecs_late > 200) {
274             debugWarning("Rather late wakeup: %lld usecs\n", usecs_late);
275         }
276         #endif
277
278         // update the x-axis values
279         m_current_time_ticks = m_next_time_ticks;
280
281         // decide what coefficients to use
282         double coeff_b, coeff_c;
283         if (m_high_bw_updates > 0) {
284             coeff_b = DLL_COEFF_B_HIGH;
285             coeff_c = DLL_COEFF_C_HIGH;
286             m_high_bw_updates--;
287             if (m_high_bw_updates == 0) {
288                 debugOutput(DEBUG_LEVEL_VERBOSE, "Switching to low-bandwidth coefficients\n");
289             }
290         } else {
291             coeff_b = DLL_COEFF_B;
292             coeff_c = DLL_COEFF_C;
293         }
294
295         // do the calculation in 'tick space'
296         int64_t tmp = (uint64_t)(coeff_b * diff_ticks_corr);
297         if(m_dll_e2 > 0) {
298             tmp = addTicks(tmp, (uint64_t)m_dll_e2);
299         } else {
300             tmp = substractTicks(tmp, (uint64_t)(-m_dll_e2));
301         }
302         if(tmp < 0) {
303             debugWarning("negative slope: %lld!\n", tmp);
304         }
305         m_next_time_ticks = addTicks((uint64_t)m_current_time_ticks, tmp);
306
307         // it should be ok to not do this in tick space since it's value
308         // is approx equal to the rate, being 24.576 ticks/usec
309         m_dll_e2 += coeff_c * diff_ticks_corr;
310
311         // For jitter graphs
312         // debugOutputShort(DEBUG_LEVEL_NORMAL, "0123456789 %f %f %f %lld %lld %lld\n",
313         //                 diff_ticks, diff_ticks_corr, m_dll_e2, cycle_timer_ticks, (int64_t)m_next_time_ticks, usecs_late);
314
315         // update the y-axis values
316         m_current_time_usecs = m_next_time_usecs;
317         m_next_time_usecs += m_usecs_per_update;
318
319         debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, " usecs: current: %f next: %f usecs_late=%lld ticks_late=%lld\n",
320                             m_current_time_usecs, m_next_time_usecs, usecs_late, ticks_late);
321         debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, " ticks: current: %f next: %f diff=%f\n",
322                             m_current_time_ticks, m_next_time_ticks, diff_ticks);
323         debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, " state: local: %11llu, dll_e2: %f, rate: %f\n",
324                             local_time, m_dll_e2, getRate());
325     }
326
327     // FIXME: priority inversion possible, run this at higher prio than client threads
328     ENTER_CRITICAL_SECTION;
329     m_current_vars.ticks = (uint64_t)(m_current_time_ticks);
330     m_current_vars.usecs = (uint64_t)m_current_time_usecs;
331     m_current_vars.rate = getRate();
332     EXIT_CRITICAL_SECTION;
333
334     return true;
335 }
336
337 uint32_t
338 CycleTimerHelper::getCycleTimerTicks()
339 {
340     uint64_t now = m_Parent.getCurrentTimeAsUsecs();
341     return getCycleTimerTicks(now);
342 }
343
344 uint32_t
345 CycleTimerHelper::getCycleTimerTicks(uint64_t now)
346 {
347     uint32_t retval;
348     struct compute_vars my_vars;
349
350     // reduce lock contention
351     ENTER_CRITICAL_SECTION;
352     my_vars = m_current_vars;
353     EXIT_CRITICAL_SECTION;
354
355     int64_t time_diff = now - my_vars.usecs;
356     double y_step_in_ticks = ((double)time_diff) * my_vars.rate;
357     int64_t y_step_in_ticks_int = (int64_t)y_step_in_ticks;
358     uint64_t offset_in_ticks_int = my_vars.ticks;
359
360     if (y_step_in_ticks_int > 0) {
361         retval = addTicks(offset_in_ticks_int, y_step_in_ticks_int);
362         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "y_step_in_ticks_int > 0: %lld, time_diff: %f, rate: %f, retval: %lu\n",
363                     y_step_in_ticks_int, time_diff, my_vars.rate, retval);
364     } else {
365         retval = substractTicks(offset_in_ticks_int, -y_step_in_ticks_int);
366
367         // this can happen if the update thread was woken up earlier than it should have been
368         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "y_step_in_ticks_int <= 0: %lld, time_diff: %f, rate: %f, retval: %lu\n",
369                     y_step_in_ticks_int, time_diff, my_vars.rate, retval);
370     }
371
372     return retval;
373 }
374
375 uint32_t
376 CycleTimerHelper::getCycleTimer()
377 {
378     uint64_t now = m_Parent.getCurrentTimeAsUsecs();
379     return getCycleTimer(now);
380 }
381
382 uint32_t
383 CycleTimerHelper::getCycleTimer(uint64_t now)
384 {
385     uint32_t ticks = getCycleTimerTicks(now);
386     uint32_t result = TICKS_TO_CYCLE_TIMER(ticks);
387 #ifdef DEBUG
388     if(CYCLE_TIMER_TO_TICKS(result) != ticks) {
389         debugWarning("Bad ctr conversion");
390     }
391 #endif
392     return result;
393 }
394
395 #else
396
397 float
398 CycleTimerHelper::getRate()
399 {
400     return getNominalRate();
401 }
402
403 float
404 CycleTimerHelper::getNominalRate()
405 {
406     float rate = ((double)TICKS_PER_SECOND) / 1000000.0;
407     return rate;
408 }
409
410 bool
411 CycleTimerHelper::Execute()
412 {
413     usleep(1000*1000);
414     return true;
415 }
416
417 uint32_t
418 CycleTimerHelper::getCycleTimerTicks()
419 {
420     return CYCLE_TIMER_TO_TICKS(getCycleTimer());
421 }
422
423 uint32_t
424 CycleTimerHelper::getCycleTimerTicks(uint64_t now)
425 {
426     debugWarning("not implemented!\n");
427     return getCycleTimerTicks();
428 }
429
430 uint32_t
431 CycleTimerHelper::getCycleTimer()
432 {
433     uint32_t cycle_timer;
434     uint64_t local_time;
435     readCycleTimerWithRetry(&cycle_timer, &local_time, 10);
436     return cycle_timer;
437 }
438
439 uint32_t
440 CycleTimerHelper::getCycleTimer(uint64_t now)
441 {
442     debugWarning("not implemented!\n");
443     return getCycleTimer();
444 }
445
446 #endif
447
448 bool
449 CycleTimerHelper::readCycleTimerWithRetry(uint32_t *cycle_timer, uint64_t *local_time, int ntries)
450 {
451     bool good=false;
452     int maxtries = ntries;
453
454     do {
455         // the ctr read can return 0 sometimes. if that happens, reread the ctr.
456         int maxtries2=ntries;
457         do {
458             if(!m_Parent.readCycleTimerReg(cycle_timer, local_time)) {
459                 debugError("Could not read cycle timer register\n");
460                 return false;
461             }
462             if (*cycle_timer == 0) {
463                 debugOutput(DEBUG_LEVEL_VERBOSE,
464                            "Bogus CTR: %08X on try %02d\n",
465                            *cycle_timer, maxtries2);
466             }
467         } while (*cycle_timer == 0 && maxtries2--);
468        
469         // catch bogus ctr reads (can happen)
470         uint64_t cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(*cycle_timer);
471    
472         if (diffTicks(cycle_timer_ticks, m_cycle_timer_ticks_prev) < 0) {
473             debugOutput( DEBUG_LEVEL_VERY_VERBOSE,
474                         "non-monotonic CTR (try %02d): %llu -> %llu\n",
475                         maxtries, m_cycle_timer_ticks_prev, cycle_timer_ticks);
476             debugOutput( DEBUG_LEVEL_VERY_VERBOSE,
477                         "                            : %08X -> %08X\n",
478                         m_cycle_timer_prev, *cycle_timer);
479             debugOutput( DEBUG_LEVEL_VERY_VERBOSE,
480                         " current: %011llu (%03us %04ucy %04uticks)\n",
481                         cycle_timer_ticks,
482                         (unsigned int)TICKS_TO_SECS( cycle_timer_ticks ),
483                         (unsigned int)TICKS_TO_CYCLES( cycle_timer_ticks ),
484                         (unsigned int)TICKS_TO_OFFSET( cycle_timer_ticks ) );
485             debugOutput( DEBUG_LEVEL_VERY_VERBOSE,
486                         " prev   : %011llu (%03us %04ucy %04uticks)\n",
487                         m_cycle_timer_ticks_prev,
488                         (unsigned int)TICKS_TO_SECS( m_cycle_timer_ticks_prev ),
489                         (unsigned int)TICKS_TO_CYCLES( m_cycle_timer_ticks_prev ),
490                         (unsigned int)TICKS_TO_OFFSET( m_cycle_timer_ticks_prev ) );
491         } else {
492             good = true;
493             m_cycle_timer_prev = *cycle_timer;
494             m_cycle_timer_ticks_prev = cycle_timer_ticks;
495         }
496     } while (!good && maxtries--);
497     return true;
498 }
499
500 void
501 CycleTimerHelper::setVerboseLevel(int l)
502 {
503     setDebugLevel(l);
504 }
Note: See TracBrowser for help on using the browser.