root/trunk/libffado/src/libieee1394/cycletimer.h

Revision 1090, 16.8 kB (checked in by ppalmers, 13 years ago)

fix CTR reconstruction bug

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 /* Definitions and utility macro's to handle the ISO cycle timer */
25
26 #ifndef __CYCLETIMER_H__
27 #define __CYCLETIMER_H__
28
29 #include "debugmodule/debugmodule.h"
30
31 #include <inttypes.h>
32
33 #define CSR_CYCLE_TIME            0x200
34 #define CSR_REGISTER_BASE  0xfffff0000000ULL
35
36 #define CYCLES_PER_SECOND   8000U
37 #define TICKS_PER_CYCLE     3072U
38 #define TICKS_PER_HALFCYCLE (3072U/2U)
39 #define TICKS_PER_SECOND    24576000UL
40 #define TICKS_PER_USEC     (24.576000)
41
42 #define USECS_PER_TICK     (1.0/TICKS_PER_USEC)
43 #define USECS_PER_CYCLE    (125U)
44
45 #define CYCLE_TIMER_GET_SECS(x)   ((((x) & 0xFE000000UL) >> 25))
46 #define CYCLE_TIMER_GET_CYCLES(x) ((((x) & 0x01FFF000UL) >> 12))
47 #define CYCLE_TIMER_GET_OFFSET(x)  ((((x) & 0x00000FFFUL)))
48 #define CYCLE_TIMER_TO_TICKS(x) ((CYCLE_TIMER_GET_SECS(x)   * TICKS_PER_SECOND) +\
49                                    (CYCLE_TIMER_GET_CYCLES(x) * TICKS_PER_CYCLE ) +\
50                                    (CYCLE_TIMER_GET_OFFSET(x)            ))
51
52 // non-efficient versions, to be avoided in critical code
53 #define TICKS_TO_SECS(x) ((x)/TICKS_PER_SECOND)
54 #define TICKS_TO_CYCLES(x) (((x)/TICKS_PER_CYCLE) % CYCLES_PER_SECOND)
55 #define TICKS_TO_OFFSET(x) (((x)%TICKS_PER_CYCLE))
56
57 #define TICKS_TO_CYCLE_TIMER(x) (  ((TICKS_TO_SECS(x) & 0x7F) << 25) \
58                                  | ((TICKS_TO_CYCLES(x) & 0x1FFF) << 12) \
59                                  | ((TICKS_TO_OFFSET(x) & 0xFFF)))
60
61 #define TICKS_TO_SYT(x)         (((TICKS_TO_CYCLES(x) & 0xF) << 12) \
62                                  | ((TICKS_TO_OFFSET(x) & 0xFFF)))
63
64 #define CYCLE_TIMER_UNWRAP_TICKS(x) (((uint64_t)(x)) \
65                                        + (127ULL * TICKS_PER_SECOND) \
66                                        + (CYCLES_PER_SECOND * TICKS_PER_CYCLE) \
67                                        + (TICKS_PER_CYCLE) \
68                                       )
69 #define CYCLE_TIMER_WRAP_TICKS(x) ((x % TICKS_PER_SECOND))
70
71 #define INVALID_TIMESTAMP_TICKS     0xFFFFFFFFFFFFFFFFULL
72
73 DECLARE_GLOBAL_DEBUG_MODULE;
74
75 /**
76  * @brief Wraps x to the maximum number of ticks
77  *
78  * The input value is wrapped to the maximum value of the cycle
79  * timer, in ticks (128sec * 24576000 ticks/sec).
80  *
81  * @param x time to wrap
82  * @return wrapped time
83  */
84 static inline uint64_t wrapAtMaxTicks(uint64_t x) {
85     if (x >= TICKS_PER_SECOND * 128L) {
86         x -= TICKS_PER_SECOND * 128L;
87     }
88
89 #ifdef DEBUG
90         if (x >= TICKS_PER_SECOND * 128L) {
91             debugWarning("insufficient wrapping: %llu\n",x);
92         }
93 #endif
94
95     return x;
96 }
97
98 /**
99  * @brief Wraps x to the minimum number of ticks
100  *
101  * The input value is wrapped to the minimum value of the cycle
102  * timer, in ticks (= 0).
103  *
104  * @param x time to wrap
105  * @return wrapped time
106  */
107 static inline int64_t wrapAtMinTicks(int64_t x) {
108     if (x < 0) {
109         x += TICKS_PER_SECOND * 128L;
110     }
111
112 #ifdef DEBUG
113         if (x < 0) {
114             debugWarning("insufficient wrapping: %lld\n",x);
115
116             debugWarning("correcting...\n");
117             while (x < 0) {
118                 x += TICKS_PER_SECOND * 128L;
119
120                 if (x < 0) {
121                     debugWarning(" insufficient wrapping: %lld\n",x);
122                 }
123             }
124         }
125
126 #endif
127
128     return (int64_t)x;
129 }
130
131 /**
132  * @brief Wraps both at minimum and maximum value for ticks
133  *
134  * The input value is wrapped to the maximum value of the cycle
135  * timer, in ticks (128sec * 24576000 ticks/sec), and
136  * to the minimum value of the cycle timer, in ticks (= 0).
137  *
138  * @param x value to wrap
139  * @return wrapped value
140  */
141 static inline int64_t wrapAtMinMaxTicks(int64_t x) {
142
143     if (x < 0) {
144         x += TICKS_PER_SECOND * 128L;
145     } else if (x >= (int64_t)(TICKS_PER_SECOND * 128L)) {
146         x -= TICKS_PER_SECOND * 128L;
147     }
148
149 #ifdef DEBUG
150         if (x >= (int64_t)(TICKS_PER_SECOND * 128L)) {
151             debugWarning("insufficient wrapping (max): %llu\n",x);
152         }
153         if (x < 0) {
154             debugWarning("insufficient wrapping (min): %lld\n",x);
155         }
156 #endif
157     return x;
158
159 }
160
161 /**
162  * @brief Computes the sum of two cycle values
163  *
164  * This function computes a sum between cycles
165  * such that it respects wrapping (at 8000 cycles).
166  *
167  * The passed arguments are assumed to be valid cycle numbers,
168  * i.e. they should be wrapped at 8000 cycles
169  *
170  * See addTicks
171  *
172  * @param x First cycle value
173  * @param y Second cycle value
174  * @return the sum x+y, wrapped
175  */
176 static inline unsigned int addCycles(unsigned int x, unsigned int y) {
177     unsigned int sum = x + y;
178 #ifdef DEBUG
179     if (x >= CYCLES_PER_SECOND || y >= CYCLES_PER_SECOND ) {
180         debugWarning("At least one argument not wrapped correctly: x=%u, y=%u\n",x,y);
181     }
182 #endif
183
184     // since both x and y are < CYCLES_PER_SECOND this should be enough to unwrap
185     if (sum > CYCLES_PER_SECOND) sum -= CYCLES_PER_SECOND;
186     return sum;
187 }
188
189 /**
190  * @brief Computes a difference between cycles
191  *
192  * This function computes a difference between cycles
193  * such that it respects wrapping (at 8000 cycles).
194  *
195  * See diffTicks
196  *
197  * @param x First cycle value
198  * @param y Second cycle value
199  * @return the difference x-y, unwrapped
200  */
201 static inline int diffCycles(unsigned int x, unsigned int y) {
202     int diff = (int)x - (int)y;
203
204     // the maximal difference we allow (4000 cycles)
205     const int max=CYCLES_PER_SECOND/2;
206
207     if(diff > max) {
208         diff -= CYCLES_PER_SECOND;
209     } else if (diff < -max) {
210         diff += CYCLES_PER_SECOND;
211     }
212
213     return diff;
214 }
215
216 /**
217  * @brief Computes a difference between timestamps
218  *
219  * This function computes a difference between timestamps
220  * such that it respects wrapping.
221  *
222  * If x wraps around, but y doesn't, the result of x-y is
223  * negative and very large. However the real difference is
224  * not large. It can be calculated by unwrapping x and then
225  * calculating x-y.
226  *
227  * @param x First timestamp
228  * @param y Second timestamp
229  * @return the difference x-y, unwrapped
230  */
231 static inline int64_t diffTicks(int64_t x, int64_t y) {
232     int64_t diff=(int64_t)x - (int64_t)y;
233
234     // the maximal difference we allow (64secs)
235     const int64_t wrapvalue=((int64_t)TICKS_PER_SECOND)*128LL;
236     const int64_t max=wrapvalue/2LL;
237
238     if(diff > max) {
239         // this means that y has wrapped, but
240         // x has not. we should unwrap y
241         // by adding TICKS_PER_SECOND*128L, meaning that we should substract
242         // this value from diff
243         diff -= wrapvalue;
244     } else if (diff < -max) {
245         // this means that x has wrapped, but
246         // y has not. we should unwrap x
247         // by adding TICKS_PER_SECOND*128L, meaning that we should add
248         // this value to diff
249         diff += wrapvalue;
250     }
251
252 #ifdef DEBUG
253     if(diff > max || diff < -max) {
254         debugWarning("difference does not make any sense\n");
255         debugWarning("diff=%lld max=%lld\n", diff, max);
256        
257     }
258 #endif
259
260     return (int64_t)diff;
261
262 }
263
264 /**
265  * @brief Computes a sum of timestamps
266  *
267  * This function computes a sum of timestamps in ticks,
268  * wrapping the result if necessary.
269  *
270  * @param x First timestamp
271  * @param y Second timestamp
272  * @return the sum x+y, wrapped
273  */
274 static inline uint64_t addTicks(uint64_t x, uint64_t y) {
275     uint64_t sum=x+y;
276
277     return wrapAtMaxTicks(sum);
278 }
279
280 /**
281  * @brief Computes a substraction of timestamps
282  *
283  * This function computes a substraction of timestamps in ticks,
284  * wrapping the result if necessary.
285  *
286  * @param x First timestamp
287  * @param y Second timestamp
288  * @return the difference x-y, wrapped
289  */
290 static inline uint64_t substractTicks(uint64_t x, uint64_t y) {
291     int64_t subs=x-y;
292
293     return wrapAtMinTicks(subs);
294 }
295
296 /**
297  * @brief Converts a received SYT timestamp to a full timestamp in ticks.
298  *
299  *
300  * @param syt_timestamp The SYT timestamp as present in the packet
301  * @param rcv_cycle The cycle this timestamp was received on
302  * @param ctr_now The current value of the cycle timer ('now')
303  * @return
304  */
305 static inline uint64_t sytRecvToFullTicks(uint64_t syt_timestamp, unsigned int rcv_cycle, uint64_t ctr_now) {
306     uint64_t timestamp;
307
308     debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "SYT=%04llX CY=%u CTR=%08llX\n",
309                        syt_timestamp, rcv_cycle, ctr_now);
310
311     // reconstruct the full cycle
312     uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now);
313     uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now);
314
315     // check for bogus ctr
316     // the cycle timer should be ahead of the receive timer
317     int diff_cycles = diffCycles(cc_cycles, rcv_cycle);
318     if (diff_cycles<0) {
319         debugWarning("current cycle timer not ahead of receive cycle: rcv: %u / cc: %llu (%d)\n",
320                         rcv_cycle, cc_cycles, diff_cycles);
321     }
322
323     // the cycletimer has wrapped since this packet was received
324     // we want cc_seconds to reflect the 'seconds' at the point this
325     // was received
326     if (rcv_cycle>cc_cycles && (diff_cycles>=0)) {
327         if (cc_seconds) {
328             cc_seconds--;
329         } else {
330             // seconds has wrapped around, so we'd better not substract 1
331             // the good value is 127
332             cc_seconds=127;
333         }
334     }
335
336     // reconstruct the top part of the timestamp using the current cycle number
337     uint64_t rcv_cycle_masked=rcv_cycle & 0xF;
338     uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp);
339
340     // if this is true, wraparound has occurred, undo this wraparound
341     if(syt_cycle<rcv_cycle_masked) syt_cycle += 0x10;
342
343     // this is the difference in cycles wrt the cycle the
344     // timestamp was received
345     uint64_t delta_cycles=syt_cycle-rcv_cycle_masked;
346
347     // reconstruct the cycle part of the timestamp
348     uint64_t new_cycles=rcv_cycle + delta_cycles;
349
350     // if the cycles cause a wraparound of the cycle timer,
351     // perform this wraparound
352     // and convert the timestamp into ticks
353     if(new_cycles<8000) {
354         timestamp  = new_cycles * TICKS_PER_CYCLE;
355     } else {
356         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
357                            "Detected wraparound: %d + %d = %d\n",
358                            rcv_cycle, delta_cycles, new_cycles);
359
360         new_cycles-=8000; // wrap around
361 #ifdef DEBUG
362         if (new_cycles >= 8000) {
363             debugWarning("insufficient unwrapping\n");
364         }
365 #endif
366         timestamp  = new_cycles * TICKS_PER_CYCLE;
367         // add one second due to wraparound
368         timestamp += TICKS_PER_SECOND;
369     }
370
371     timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp);
372
373     timestamp = addTicks(timestamp, cc_seconds * TICKS_PER_SECOND);
374
375     #ifdef DEBUG
376         if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) {
377             debugWarning("back-converted timestamp not equal to SYT\n");
378             debugWarning("TS=%011llu TSC=%08lX SYT=%04X\n",
379                   timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp);
380         }
381     #endif
382
383     return timestamp;
384 }
385
386 /**
387  * @brief Converts a received SYT timestamp to a full timestamp in ticks.
388  *
389  *
390  * @param syt_timestamp The SYT timestamp as present in the packet
391  * @param rcv_ctr The CTR value this timestamp was received on (offset can be 0)
392  * @return
393  */
394 static inline uint64_t sytRecvToFullTicks2(uint64_t syt_timestamp, uint32_t rcv_ctr) {
395     uint64_t timestamp;
396
397     debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "SYT=%04llX RCV_CTR=%08X\n",
398                        syt_timestamp, rcv_ctr);
399
400     // reconstruct the top part of the timestamp using the current cycle number
401     unsigned int rcv_cycle = CYCLE_TIMER_GET_CYCLES(rcv_ctr);
402     unsigned int rcv_cycle_masked = rcv_cycle & 0xF;
403     unsigned int syt_cycle = CYCLE_TIMER_GET_CYCLES(syt_timestamp);
404
405     // if this is true, wraparound has occurred, undo this wraparound
406     if(syt_cycle<rcv_cycle_masked) syt_cycle += 0x10;
407
408     // this is the difference in cycles wrt the cycle the
409     // timestamp was received
410     unsigned int delta_cycles = syt_cycle - rcv_cycle_masked;
411
412     // reconstruct the cycle part of the timestamp
413     rcv_cycle += delta_cycles;
414
415     // if the cycles cause a wraparound of the cycle timer,
416     // perform this wraparound
417     // and convert the timestamp into ticks
418     if(rcv_cycle<8000) {
419         timestamp = rcv_cycle * TICKS_PER_CYCLE;
420     } else {
421         rcv_cycle -= 8000; // wrap around
422 #ifdef DEBUG
423         if (rcv_cycle >= 8000) {
424             debugWarning("insufficient unwrapping\n");
425         }
426 #endif
427         timestamp  = rcv_cycle * TICKS_PER_CYCLE;
428         // add one second due to wraparound
429         timestamp += TICKS_PER_SECOND;
430     }
431
432     timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp);
433
434     timestamp = addTicks(timestamp, CYCLE_TIMER_GET_SECS(rcv_ctr) * TICKS_PER_SECOND);
435
436     #ifdef DEBUG
437         if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) {
438             debugWarning("back-converted timestamp not equal to SYT\n");
439             debugWarning("TS=%011llu TSC=%08lX SYT=%04X\n",
440                   timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp);
441         }
442     #endif
443
444     return timestamp;
445 }
446
447 /**
448  * @brief Converts a transmit SYT timestamp to a full timestamp in ticks.
449  *
450  * The difference between sytRecvToFullTicks and sytXmitToFullTicks is
451  * the way SYT cycle wraparound is detected: in the receive version,
452  * wraparound is present if rcv_cycle > current_cycle. In the xmit
453  * version this is when current_cycle > xmt_cycle.
454  *
455  * @param syt_timestamp The SYT timestamp as present in the packet
456  * @param xmt_cycle The cycle this timestamp was received on
457  * @param ctr_now The current value of the cycle timer ('now')
458  * @return
459  */
460 static inline uint64_t sytXmitToFullTicks(uint64_t syt_timestamp, unsigned int xmt_cycle, uint64_t ctr_now) {
461     uint64_t timestamp;
462
463     debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "SYT=%08llX CY=%04X CTR=%08llX\n",
464                        syt_timestamp, xmt_cycle, ctr_now);
465
466     // reconstruct the full cycle
467     uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now);
468     uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now);
469
470     // check for bogus CTR
471     int diff_cycles = diffCycles(xmt_cycle, cc_cycles);
472     if (diff_cycles<0) {
473         debugWarning("xmit cycle not ahead of current cycle: xmt: %u / cc: %llu (%d)\n",
474                         xmt_cycle, cc_cycles, diff_cycles);
475     }
476
477     // the cycletimer has wrapped since this packet was received
478     // we want cc_seconds to reflect the 'seconds' at the point this
479     // is to be transmitted
480     if (cc_cycles>xmt_cycle && (diff_cycles>=0)) {
481         if (cc_seconds) {
482             cc_seconds--;
483         } else {
484             // seconds has wrapped around, so we'd better not substract 1
485             // the good value is 127
486             cc_seconds=127;
487         }
488     }
489
490     // reconstruct the top part of the timestamp using the current cycle number
491     uint64_t xmt_cycle_masked=xmt_cycle & 0xF;
492     uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp);
493
494     // if this is true, wraparound has occurred, undo this wraparound
495     if(syt_cycle<xmt_cycle_masked) syt_cycle += 0x10;
496
497     // this is the difference in cycles wrt the cycle the
498     // timestamp was received
499     uint64_t delta_cycles=syt_cycle-xmt_cycle_masked;
500
501     // reconstruct the cycle part of the timestamp
502     uint64_t new_cycles=xmt_cycle + delta_cycles;
503
504     // if the cycles cause a wraparound of the cycle timer,
505     // perform this wraparound
506     // and convert the timestamp into ticks
507     if(new_cycles<8000) {
508         timestamp  = new_cycles * TICKS_PER_CYCLE;
509     } else {
510         debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE,
511                            "Detected wraparound: %d + %d = %d\n",
512                            xmt_cycle, delta_cycles, new_cycles);
513
514         new_cycles-=8000; // wrap around
515 #ifdef DEBUG
516         if (new_cycles >= 8000) {
517             debugWarning("insufficient unwrapping\n");
518         }
519 #endif
520         timestamp  = new_cycles * TICKS_PER_CYCLE;
521         // add one second due to wraparound
522         timestamp += TICKS_PER_SECOND;
523     }
524
525     timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp);
526
527     timestamp = addTicks(timestamp, cc_seconds * TICKS_PER_SECOND);
528
529     #ifdef DEBUG
530         if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) {
531             debugWarning("back-converted timestamp not equal to SYT\n");
532             debugWarning("TS=%011llu TSC=%08lX SYT=%04X\n",
533                   timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp);
534         }
535     #endif
536
537     return timestamp;
538 }
539
540 #endif // __CYCLETIMER_H__
Note: See TracBrowser for help on using the browser.