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

Revision 1763, 17.0 kB (checked in by ppalmers, 11 years ago)

Merged revisions 1536,1541,1544-1546,1549,1554-1562,1571,1579-1581,1618,1632,1634-1635,1661,1677-1679,1703-1704,1715,1720-1723,1743-1745,1755 via svnmerge from
svn+ssh://ffadosvn@ffado.org/ffado/branches/libffado-2.0

Also fix remaining format string warnings.

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