root/branches/ppalmers-streaming/src/libstreaming/util/cycletimer.h

Revision 712, 14.0 kB (checked in by ppalmers, 13 years ago)

almost there...

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 library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License version 2.1, as published by the Free Software Foundation;
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  * MA 02110-1301 USA
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.