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

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

- code cleanup
- make transmit handler AMDTP compliant (don't send too much in advance)

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