root/branches/libffado-2.0/src/libieee1394/cycletimer.h

Revision 1247, 17.1 kB (checked in by ppalmers, 13 years ago)

merge trunk changes r1235:1246 (svn merge -r 1234:HEAD svn+ssh://ffadosvn@ffado.org/ffado/trunk/libffado)

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