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

Revision 703, 13.2 kB (checked in by ppalmers, 16 years ago)

- rework of streaming startup
- started moving files around to enhance structure

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