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

Revision 752, 8.7 kB (checked in by ppalmers, 13 years ago)

- Implement a DLL based mechanism to read the cycle timer. This can potentially be more lightweight for the reader threads since it avoids a the CTR read kernel call. It also has the
side effect that FFADO now works on older kernels that don't implement the cycle timer read call.

Line 
1 /*
2  * Copyright (C) 2005-2007 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 3 of the License, or
12  * (at your option) any later version.
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 "CycleTimerHelper.h"
25 #include "ieee1394service.h"
26 #include "libutil/PosixThread.h"
27
28 #define DLL_BANDWIDTH (0.01)
29 #define DLL_PI        (3.141592653589793238)
30 #define DLL_SQRT2     (1.414213562373095049)
31 #define DLL_OMEGA     (2.0*DLL_PI*DLL_BANDWIDTH)
32 #define DLL_COEFF_B   (DLL_SQRT2 * DLL_OMEGA)
33 #define DLL_COEFF_C   (DLL_OMEGA * DLL_OMEGA)
34
35 IMPL_DEBUG_MODULE( CycleTimerHelper, CycleTimerHelper, DEBUG_LEVEL_NORMAL );
36
37 CycleTimerHelper::CycleTimerHelper(Ieee1394Service &parent, unsigned int update_period_us)
38     : m_Parent ( parent )
39     , m_ticks_per_update ( ((uint64_t)TICKS_PER_SECOND) * ((uint64_t)update_period_us) / 1000000ULL )
40     , m_usecs_per_update ( update_period_us )
41     , m_avg_wakeup_delay ( 0.0 )
42     , m_dll_e2 ( 0.0 )
43     , m_current_time_usecs ( 0 )
44     , m_next_time_usecs ( 0 )
45     , m_current_time_ticks ( 0 )
46     , m_next_time_ticks ( 0 )
47     , m_first_run ( true )
48     , m_Thread ( NULL )
49     , m_realtime ( false )
50     , m_priority ( 0 )
51 {
52     debugOutput( DEBUG_LEVEL_VERBOSE, "Create %p...\n", this);
53 }
54
55 CycleTimerHelper::CycleTimerHelper(Ieee1394Service &parent, unsigned int update_period_us, bool rt, int prio)
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_Thread ( NULL )
67     , m_realtime ( rt )
68     , m_priority ( prio )
69 {
70     debugOutput( DEBUG_LEVEL_VERBOSE, "Create %p...\n", this);
71 }
72
73 CycleTimerHelper::~CycleTimerHelper()
74 {
75     if (m_Thread) {
76         m_Thread->Stop();
77         delete m_Thread;
78     }
79 }
80
81 bool
82 CycleTimerHelper::Start()
83 {
84     debugOutput( DEBUG_LEVEL_VERBOSE, "Start %p...\n", this);
85     m_Thread = new Util::PosixThread(this, m_realtime, m_priority,
86                                      PTHREAD_CANCEL_DEFERRED);
87     if(!m_Thread) {
88         debugFatal("Could not create update thread\n");
89     }
90     if(!m_Thread) {
91         debugFatal("No thread\n");
92         return false;
93     }
94     if (m_Thread->Start() != 0) {
95         debugFatal("Could not start update thread\n");
96         return false;
97     }
98     return true;
99 }
100
101 bool
102 CycleTimerHelper::Init()
103 {
104     debugOutput( DEBUG_LEVEL_VERBOSE, "Initialize %p...\n", this);
105     pthread_mutex_init(&m_compute_vars_lock, NULL);
106     return true;
107 }
108
109 bool
110 CycleTimerHelper::setThreadParameters(bool rt, int priority) {
111     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) (rt=%d, prio=%d)...\n", this, rt, priority);
112     if (priority > 98) priority = 98; // cap the priority
113     m_realtime = rt;
114     m_priority = priority;
115
116     if (m_Thread) {
117         if (m_realtime) {
118             m_Thread->AcquireRealTime(m_priority);
119         } else {
120             m_Thread->DropRealTime();
121         }
122     }
123     return true;
124 }
125
126 float
127 CycleTimerHelper::getRate()
128 {
129     float rate = (float)(diffTicks((uint64_t)m_next_time_ticks, (uint64_t)m_current_time_ticks));
130     rate /= (float)(m_next_time_usecs - m_current_time_usecs);
131     return rate;
132 }
133
134 float
135 CycleTimerHelper::getNominalRate()
136 {
137     float rate = ((double)TICKS_PER_SECOND) / 1000000.0;
138     return rate;
139 }
140
141 bool
142 CycleTimerHelper::Execute()
143 {
144     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Execute %p...\n", this);
145     uint32_t cycle_timer;
146     uint64_t local_time;
147     if(!m_Parent.readCycleTimerReg(&cycle_timer, &local_time)) {
148         debugError("Could not read cycle timer register\n");
149         return false;
150     }
151     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " read : CTR: %11lu, local: %17llu\n",
152                     cycle_timer, local_time);
153
154     double usecs_late;
155     if (m_first_run) {
156         usecs_late = 0.0;
157         m_dll_e2 = m_ticks_per_update;
158         m_current_time_usecs = local_time;
159         m_next_time_usecs = m_current_time_usecs + m_usecs_per_update;
160         m_current_time_ticks = CYCLE_TIMER_TO_TICKS( cycle_timer );
161         m_next_time_ticks = addTicks( (uint64_t)m_current_time_ticks, (uint64_t)m_dll_e2);
162         debugOutput( DEBUG_LEVEL_VERBOSE, " First run\n");
163         debugOutput( DEBUG_LEVEL_VERBOSE, "  usecs/update: %lu, ticks/update: %lu, m_dll_e2: %f\n",
164                                           m_usecs_per_update, m_ticks_per_update, m_dll_e2);
165         debugOutput( DEBUG_LEVEL_VERBOSE, "  usecs current: %f, next: %f\n", m_current_time_usecs, m_next_time_usecs);
166         debugOutput( DEBUG_LEVEL_VERBOSE, "  ticks current: %f, next: %f\n", m_current_time_ticks, m_next_time_ticks);
167         m_first_run = false;
168     } else {
169
170         double diff = m_next_time_usecs - m_current_time_usecs;
171         debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " usecs: local: %11llu current: %f next: %f, diff: %f\n",
172                     local_time, m_current_time_usecs, m_next_time_usecs, diff);
173
174         uint64_t cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer);
175         usecs_late = ((double)local_time) - (m_next_time_usecs);
176
177         // we update the x-axis values
178         m_current_time_usecs = m_next_time_usecs;
179         m_next_time_usecs = (local_time - usecs_late) + m_usecs_per_update;
180         debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " usecs: current: %f next: %f usecs_late=%f\n",
181                     m_current_time_usecs, m_next_time_usecs, usecs_late);
182
183         // and the y-axis values
184         double diff_ticks = diffTicks(cycle_timer_ticks, (int64_t)m_next_time_ticks);
185         m_current_time_ticks = m_next_time_ticks;
186         m_next_time_ticks = addTicks((uint64_t)m_current_time_ticks,
187                                      (uint64_t)((DLL_COEFF_B * diff_ticks) + m_dll_e2));
188         m_dll_e2 += DLL_COEFF_C * diff_ticks;
189         debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " ticks: current: %f next: %f diff=%f\n",
190                     m_current_time_ticks, m_next_time_ticks, diff_ticks);
191
192         debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " state: local: %11llu, dll_e2: %f, rate: %f\n",
193                     local_time, m_dll_e2, getRate());
194     }
195
196     // track the average wakeup delay
197     m_avg_wakeup_delay += 0.01 * usecs_late;
198
199     // FIXME: priority inversion!
200     pthread_mutex_lock(&m_compute_vars_lock);
201     m_current_vars.ticks = m_current_time_ticks;
202     m_current_vars.usecs = m_current_time_usecs;
203     m_current_vars.rate = getRate();
204     pthread_mutex_unlock(&m_compute_vars_lock);
205
206     // wait for the next update period
207     int64_t time_to_sleep = (int64_t)m_next_time_usecs - m_Parent.getCurrentTimeAsUsecs();
208     time_to_sleep -= (int64_t)m_avg_wakeup_delay;
209     //int64_t time_to_sleep = m_usecs_per_update;
210     if (time_to_sleep > 0) {
211         debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " sleeping %lld usecs (avg delay: %f)\n", time_to_sleep, m_avg_wakeup_delay);
212         usleep(time_to_sleep);
213     }
214     return true;
215 }
216
217 uint32_t
218 CycleTimerHelper::getCycleTimerTicks()
219 {
220     uint64_t now = m_Parent.getCurrentTimeAsUsecs();
221     return getCycleTimerTicks(now);
222 }
223
224 uint32_t
225 CycleTimerHelper::getCycleTimerTicks(uint64_t now)
226 {
227     uint32_t retval;
228     struct compute_vars my_vars;
229
230     // reduce lock contention
231     pthread_mutex_lock(&m_compute_vars_lock);
232     my_vars = m_current_vars;
233     pthread_mutex_unlock(&m_compute_vars_lock);
234
235     double time_diff = now - my_vars.usecs;
236     double y_step_in_ticks = time_diff * my_vars.rate;
237     int64_t y_step_in_ticks_int = (int64_t)y_step_in_ticks;
238     uint64_t offset_in_ticks_int = (uint64_t)my_vars.ticks;
239
240     if (y_step_in_ticks_int > 0) {
241         retval = addTicks(offset_in_ticks_int, y_step_in_ticks_int);
242     } else {
243         debugWarning("y_step_in_ticks_int <= 0: %lld\n", y_step_in_ticks_int);
244         retval = substractTicks(offset_in_ticks_int, -y_step_in_ticks_int);
245     }
246
247     return retval;
248 }
249
250 uint32_t
251 CycleTimerHelper::getCycleTimer()
252 {
253     return TICKS_TO_CYCLE_TIMER(getCycleTimerTicks());
254 }
255
256 uint32_t
257 CycleTimerHelper::getCycleTimer(uint64_t now)
258 {
259     return TICKS_TO_CYCLE_TIMER(getCycleTimerTicks(now));
260 }
261
262 void
263 CycleTimerHelper::setVerboseLevel(int l)
264 {
265     setDebugLevel(l);
266 }
Note: See TracBrowser for help on using the browser.