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

Revision 498, 13.1 kB (checked in by jwoithe, 17 years ago)

MOTU: more debugging and tweaks.
TimestampedBuffer?: ffado_timestamp_t changed to double for now - MOTU needs the extra precision.

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