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

Revision 864, 13.9 kB (checked in by ppalmers, 15 years ago)

update license to GPLv2 or GPLv3 instead of GPLv2 or any later version. Update copyrights to reflect the new year

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