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

Revision 705, 13.2 kB (checked in by ppalmers, 15 years ago)

restructure the streaming directory

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 a difference between timestamps
162  *
163  * This function computes a difference between timestamps
164  * such that it respects wrapping.
165  *
166  * If x wraps around, but y doesn't, the result of x-y is
167  * negative and very large. However the real difference is
168  * not large. It can be calculated by unwrapping x and then
169  * calculating x-y.
170  *
171  * @param x First timestamp
172  * @param y Second timestamp
173  * @return the difference x-y, unwrapped
174  */
175 static inline int64_t diffTicks(int64_t x, int64_t y) {
176     int64_t diff=(int64_t)x - (int64_t)y;
177
178     // the maximal difference we allow (64secs)
179     const int64_t wrapvalue=((int64_t)TICKS_PER_SECOND)*128LL;
180     const int64_t max=wrapvalue/2LL;
181
182     if(diff > max) {
183         // this means that y has wrapped, but
184         // x has not. we should unwrap y
185         // by adding TICKS_PER_SECOND*128L, meaning that we should substract
186         // this value from diff
187         diff -= wrapvalue;
188     } else if (diff < -max) {
189         // this means that x has wrapped, but
190         // y has not. we should unwrap x
191         // by adding TICKS_PER_SECOND*128L, meaning that we should add
192         // this value to diff
193         diff += wrapvalue;
194     }
195
196 #ifdef DEBUG
197     if(diff > max || diff < -max) {
198         debugWarning("difference does not make any sense\n");
199         debugWarning("diff=%lld max=%lld\n", diff, max);
200        
201     }
202 #endif
203
204     return (int64_t)diff;
205
206 }
207
208 /**
209  * @brief Computes a sum of timestamps
210  *
211  * This function computes a sum of timestamps in ticks,
212  * wrapping the result if necessary.
213  *
214  * @param x First timestamp
215  * @param y Second timestamp
216  * @return the sum x+y, wrapped
217  */
218 static inline uint64_t addTicks(uint64_t x, uint64_t y) {
219     uint64_t sum=x+y;
220
221     return wrapAtMaxTicks(sum);
222 }
223
224 /**
225  * @brief Computes a substraction of timestamps
226  *
227  * This function computes a substraction of timestamps in ticks,
228  * wrapping the result if necessary.
229  *
230  * @param x First timestamp
231  * @param y Second timestamp
232  * @return the difference x-y, wrapped
233  */
234 static inline uint64_t substractTicks(uint64_t x, uint64_t y) {
235     int64_t subs=x-y;
236
237     return wrapAtMinTicks(subs);
238 }
239
240 /**
241  * @brief Converts a received SYT timestamp to a full timestamp in ticks.
242  *
243  *
244  * @param syt_timestamp The SYT timestamp as present in the packet
245  * @param rcv_cycle The cycle this timestamp was received on
246  * @param ctr_now The current value of the cycle timer ('now')
247  * @return
248  */
249 static inline uint64_t sytRecvToFullTicks(uint64_t syt_timestamp, unsigned int rcv_cycle, uint64_t ctr_now) {
250     uint64_t timestamp;
251
252     debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"SYT=%04llX CY=%u CTR=%08llX\n",
253         syt_timestamp, rcv_cycle, ctr_now);
254
255     // reconstruct the full cycle
256     uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now);
257     uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now);
258
259     // the cycletimer has wrapped since this packet was received
260     // we want cc_seconds to reflect the 'seconds' at the point this
261     // was received
262     if (rcv_cycle>cc_cycles) {
263         if (cc_seconds) {
264             cc_seconds--;
265         } else {
266             // seconds has wrapped around, so we'd better not substract 1
267             // the good value is 127
268             cc_seconds=127;
269         }
270     }
271
272     // reconstruct the top part of the timestamp using the current cycle number
273     uint64_t rcv_cycle_masked=rcv_cycle & 0xF;
274     uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp);
275
276     // if this is true, wraparound has occurred, undo this wraparound
277     if(syt_cycle<rcv_cycle_masked) syt_cycle += 0x10;
278
279     // this is the difference in cycles wrt the cycle the
280     // timestamp was received
281     uint64_t delta_cycles=syt_cycle-rcv_cycle_masked;
282
283     // reconstruct the cycle part of the timestamp
284     uint64_t new_cycles=rcv_cycle + delta_cycles;
285
286     // if the cycles cause a wraparound of the cycle timer,
287     // perform this wraparound
288     // and convert the timestamp into ticks
289     if(new_cycles<8000) {
290         timestamp  = new_cycles * TICKS_PER_CYCLE;
291     } else {
292         debugOutput(DEBUG_LEVEL_VERY_VERBOSE,
293             "Detected wraparound: %d + %d = %d\n",
294             rcv_cycle,delta_cycles,new_cycles);
295
296         new_cycles-=8000; // wrap around
297 #ifdef DEBUG
298         if (new_cycles >= 8000) {
299             debugWarning("insufficient unwrapping\n");
300         }
301 #endif
302         timestamp  = new_cycles * TICKS_PER_CYCLE;
303         // add one second due to wraparound
304         timestamp += TICKS_PER_SECOND;
305     }
306
307     timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp);
308
309     timestamp = addTicks(timestamp, cc_seconds * TICKS_PER_SECOND);
310
311     #ifdef DEBUG
312         if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) {
313             debugWarning("back-converted timestamp not equal to SYT\n");
314             debugWarning("TS=%011llu TSC=%08lX SYT=%04X\n",
315                   timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp);
316         }
317     #endif
318
319     return timestamp;
320 }
321
322 /**
323  * @brief Converts a transmit SYT timestamp to a full timestamp in ticks.
324  *
325  * The difference between sytRecvToFullTicks and sytXmitToFullTicks is
326  * the way SYT cycle wraparound is detected: in the receive version,
327  * wraparound is present if rcv_cycle > current_cycle. In the xmit
328  * version this is when current_cycle > xmt_cycle.
329  *
330  * @param syt_timestamp The SYT timestamp as present in the packet
331  * @param xmt_cycle The cycle this timestamp was received on
332  * @param ctr_now The current value of the cycle timer ('now')
333  * @return
334  */
335 static inline uint64_t sytXmitToFullTicks(uint64_t syt_timestamp, unsigned int xmt_cycle, uint64_t ctr_now) {
336     uint64_t timestamp;
337
338     debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"SYT=%08llX CY=%04X CTR=%08llX\n",
339         syt_timestamp,xmt_cycle,ctr_now);
340
341     // reconstruct the full cycle
342     uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now);
343     uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now);
344
345     // the cycletimer has wrapped since this packet was received
346     // we want cc_seconds to reflect the 'seconds' at the point this
347     // is to be transmitted
348     if (xmt_cycle<cc_cycles) {
349         if (cc_seconds) {
350             cc_seconds--;
351         } else {
352             // seconds has wrapped around, so we'd better not substract 1
353             // the good value is 127
354             cc_seconds=127;
355         }
356     }
357
358     // reconstruct the top part of the timestamp using the current cycle number
359     uint64_t xmt_cycle_masked=xmt_cycle & 0xF;
360     uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp);
361
362     // if this is true, wraparound has occurred, undo this wraparound
363     if(syt_cycle<xmt_cycle_masked) syt_cycle += 0x10;
364
365     // this is the difference in cycles wrt the cycle the
366     // timestamp was received
367     uint64_t delta_cycles=syt_cycle-xmt_cycle_masked;
368
369     // reconstruct the cycle part of the timestamp
370     uint64_t new_cycles=xmt_cycle + delta_cycles;
371
372     // if the cycles cause a wraparound of the cycle timer,
373     // perform this wraparound
374     // and convert the timestamp into ticks
375     if(new_cycles<8000) {
376         timestamp  = new_cycles * TICKS_PER_CYCLE;
377     } else {
378         debugOutput(DEBUG_LEVEL_VERY_VERBOSE,
379             "Detected wraparound: %d + %d = %d\n",
380             xmt_cycle,delta_cycles,new_cycles);
381
382         new_cycles-=8000; // wrap around
383 #ifdef DEBUG
384         if (new_cycles >= 8000) {
385             debugWarning("insufficient unwrapping\n");
386         }
387 #endif
388         timestamp  = new_cycles * TICKS_PER_CYCLE;
389         // add one second due to wraparound
390         timestamp += TICKS_PER_SECOND;
391     }
392
393     timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp);
394
395     timestamp = addTicks(timestamp, cc_seconds * TICKS_PER_SECOND);
396
397     #ifdef DEBUG
398         if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) {
399             debugWarning("back-converted timestamp not equal to SYT\n");
400             debugWarning("TS=%011llu TSC=%08lX SYT=%04X\n",
401                   timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp);
402         }
403     #endif
404
405     return timestamp;
406 }
407
408 /**
409  * @brief Computes a difference between cycles
410  *
411  * This function computes a difference between cycles
412  * such that it respects wrapping (at 8000 cycles).
413  *
414  * See diffTicks
415  *
416  * @param x First cycle value
417  * @param y Second cycle value
418  * @return the difference x-y, unwrapped
419  */
420 static inline int diffCycles(unsigned int x, unsigned int y) {
421     int diff = x - y;
422
423     // the maximal difference we allow (64secs)
424     const int max=CYCLES_PER_SECOND/2;
425
426     if(diff > max) {
427         diff -= CYCLES_PER_SECOND;
428     } else if (diff < -max) {
429         diff += CYCLES_PER_SECOND;
430     }
431
432     return diff;
433 }
434
435 #endif // __CYCLETIMER_H__
Note: See TracBrowser for help on using the browser.