2 * Copyright (C) 2005-2008 by Pieter Palmers
4 * This file is part of FFADO
5 * FFADO = Free Firewire (pro-)audio drivers for linux
7 * FFADO is based upon FreeBoB.
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) version 3 of the License.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 /* Definitions and utility macro's to handle the ISO cycle timer */
26 #ifndef __CYCLETIMER_H__
27 #define __CYCLETIMER_H__
29 #include "debugmodule/debugmodule.h"
33 #define CSR_CYCLE_TIME 0x200
34 #define CSR_REGISTER_BASE 0xfffff0000000ULL
36 #define CYCLES_PER_SECOND 8000U
37 #define TICKS_PER_CYCLE 3072U
38 #define TICKS_PER_HALFCYCLE (3072U/2U)
39 #define TICKS_PER_SECOND 24576000UL
40 #define TICKS_PER_USEC (24.576000)
42 #define USECS_PER_TICK (1.0/TICKS_PER_USEC)
43 #define USECS_PER_CYCLE (125U)
45 #define CYCLE_TIMER_GET_SECS(x) ((((x) & 0xFE000000UL) >> 25))
46 #define CYCLE_TIMER_GET_CYCLES(x) ((((x) & 0x01FFF000UL) >> 12))
47 #define CYCLE_TIMER_GET_OFFSET(x) ((((x) & 0x00000FFFUL)))
49 #define CYCLE_TIMER_SET_SECS(v, x) (((v) & ~0xFE000000UL) | (((x) & 0x7F) << 25))
50 #define CYCLE_TIMER_SET_CYCLES(v, x) ((((v) & ~0x01FFF000UL) | (((x) & 0x1FFF) << 12)))
51 #define CYCLE_TIMER_SET_OFFSET(v, x) ((((v) & ~0x00000FFFUL) | ((x) & 0xFFF)))
54 #define CYCLE_TIMER_TO_TICKS(x) ((CYCLE_TIMER_GET_SECS(x) * TICKS_PER_SECOND) +\
55 (CYCLE_TIMER_GET_CYCLES(x) * TICKS_PER_CYCLE ) +\
56 (CYCLE_TIMER_GET_OFFSET(x) ))
58 // non-efficient versions, to be avoided in critical code
59 #define TICKS_TO_SECS(x) ((x)/TICKS_PER_SECOND)
60 #define TICKS_TO_CYCLES(x) (((x)/TICKS_PER_CYCLE) % CYCLES_PER_SECOND)
61 #define TICKS_TO_OFFSET(x) (((x)%TICKS_PER_CYCLE))
63 #define TICKS_TO_CYCLE_TIMER(x) ( ((TICKS_TO_SECS(x) & 0x7F) << 25) \
64 | ((TICKS_TO_CYCLES(x) & 0x1FFF) << 12) \
65 | ((TICKS_TO_OFFSET(x) & 0xFFF)))
67 #define TICKS_TO_SYT(x) (((TICKS_TO_CYCLES(x) & 0xF) << 12) \
68 | ((TICKS_TO_OFFSET(x) & 0xFFF)))
70 #define CYCLE_TIMER_UNWRAP_TICKS(x) (((uint64_t)(x)) \
71 + (127ULL * TICKS_PER_SECOND) \
72 + (CYCLES_PER_SECOND * TICKS_PER_CYCLE) \
75 #define CYCLE_TIMER_WRAP_TICKS(x) ((x % TICKS_PER_SECOND))
77 #define INVALID_TIMESTAMP_TICKS 0xFFFFFFFFFFFFFFFFULL
79 DECLARE_GLOBAL_DEBUG_MODULE
;
82 * @brief Wraps x to the maximum number of ticks
84 * The input value is wrapped to the maximum value of the cycle
85 * timer, in ticks (128sec * 24576000 ticks/sec).
87 * @param x time to wrap
88 * @return wrapped time
90 static inline uint64_t wrapAtMaxTicks(uint64_t x
) {
91 if (x
>= TICKS_PER_SECOND
* 128L) {
92 x
-= TICKS_PER_SECOND
* 128L;
96 if (x
>= TICKS_PER_SECOND
* 128L) {
97 debugWarning("insufficient wrapping: %"PRIu64
"\n",x
);
105 * @brief Wraps x to the minimum number of ticks
107 * The input value is wrapped to the minimum value of the cycle
108 * timer, in ticks (= 0).
110 * @param x time to wrap
111 * @return wrapped time
113 static inline int64_t wrapAtMinTicks(int64_t x
) {
115 x
+= TICKS_PER_SECOND
* 128L;
120 debugWarning("insufficient wrapping: %"PRId64
"\n",x
);
128 * @brief Wraps both at minimum and maximum value for ticks
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).
134 * @param x value to wrap
135 * @return wrapped value
137 static inline int64_t wrapAtMinMaxTicks(int64_t x
) {
140 x
+= TICKS_PER_SECOND
* 128L;
141 } else if (x
>= (int64_t)(TICKS_PER_SECOND
* 128L)) {
142 x
-= TICKS_PER_SECOND
* 128L;
146 if (x
>= (int64_t)(TICKS_PER_SECOND
* 128L)) {
147 debugWarning("insufficient wrapping (max): %"PRIu64
"\n",x
);
150 debugWarning("insufficient wrapping (min): %"PRId64
"\n",x
);
158 * @brief Computes the sum of two cycle values
160 * This function computes a sum between cycles
161 * such that it respects wrapping (at 8000 cycles).
163 * The passed arguments are assumed to be valid cycle numbers,
164 * i.e. they should be wrapped at 8000 cycles
168 * @param x First cycle value
169 * @param y Second cycle value
170 * @return the sum x+y, wrapped
172 static inline unsigned int addCycles(unsigned int x
, unsigned int y
) {
173 unsigned int sum
= x
+ y
;
175 if (x
>= CYCLES_PER_SECOND
|| y
>= CYCLES_PER_SECOND
) {
176 debugWarning("At least one argument not wrapped correctly: x=%u, y=%u\n",x
,y
);
180 // since both x and y are < CYCLES_PER_SECOND this should be enough to unwrap
181 if (sum
> CYCLES_PER_SECOND
) sum
-= CYCLES_PER_SECOND
;
186 * @brief Computes a difference between cycles
188 * This function computes a difference between cycles
189 * such that it respects wrapping (at 8000 cycles).
193 * @param x First cycle value
194 * @param y Second cycle value
195 * @return the difference x-y, unwrapped
197 static inline int diffCycles(unsigned int x
, unsigned int y
) {
198 int diff
= (int)x
- (int)y
;
200 // the maximal difference we allow (4000 cycles)
201 const int max
=CYCLES_PER_SECOND
/2;
204 diff
-= CYCLES_PER_SECOND
;
205 } else if (diff
< -max
) {
206 diff
+= CYCLES_PER_SECOND
;
213 * @brief Computes a difference between timestamps
215 * This function computes a difference between timestamps
216 * such that it respects wrapping.
218 * If x wraps around, but y doesn't, the result of x-y is
219 * negative and very large. However the real difference is
220 * not large. It can be calculated by unwrapping x and then
223 * @param x First timestamp
224 * @param y Second timestamp
225 * @return the difference x-y, unwrapped
227 static inline int64_t diffTicks(int64_t x
, int64_t y
) {
228 int64_t diff
=(int64_t)x
- (int64_t)y
;
230 // the maximal difference we allow (64secs)
231 const int64_t wrapvalue
=((int64_t)TICKS_PER_SECOND
)*128LL;
232 const int64_t max
=wrapvalue
/2LL;
235 // this means that y has wrapped, but
236 // x has not. we should unwrap y
237 // by adding TICKS_PER_SECOND*128L, meaning that we should substract
238 // this value from diff
240 } else if (diff
< -max
) {
241 // this means that x has wrapped, but
242 // y has not. we should unwrap x
243 // by adding TICKS_PER_SECOND*128L, meaning that we should add
244 // this value to diff
249 if(diff
> max
|| diff
< -max
) {
250 debugWarning("difference does not make any sense\n");
251 debugWarning("diff=%"PRId64
" max=%"PRId64
"\n", diff
, max
);
256 return (int64_t)diff
;
261 * @brief Computes a sum of timestamps
263 * This function computes a sum of timestamps in ticks,
264 * wrapping the result if necessary.
266 * @param x First timestamp
267 * @param y Second timestamp
268 * @return the sum x+y, wrapped
270 static inline uint64_t addTicks(uint64_t x
, uint64_t y
) {
273 return wrapAtMaxTicks(sum
);
277 * @brief Computes a substraction of timestamps
279 * This function computes a substraction of timestamps in ticks,
280 * wrapping the result if necessary.
282 * @param x First timestamp
283 * @param y Second timestamp
284 * @return the difference x-y, wrapped
286 static inline uint64_t substractTicks(uint64_t x
, uint64_t y
) {
289 return wrapAtMinTicks(subs
);
293 * @brief Converts a received SYT timestamp to a full timestamp in ticks.
296 * @param syt_timestamp The SYT timestamp as present in the packet
297 * @param rcv_cycle The cycle this timestamp was received on
298 * @param ctr_now The current value of the cycle timer ('now')
301 static inline uint64_t sytRecvToFullTicks(uint64_t syt_timestamp
, unsigned int rcv_cycle
, uint64_t ctr_now
) {
304 debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE
, "SYT=%"PRIX64
" CY=%u CTR=%08"PRIX64
"\n",
305 syt_timestamp
, rcv_cycle
, ctr_now
);
307 // reconstruct the full cycle
308 uint64_t cc_cycles
=CYCLE_TIMER_GET_CYCLES(ctr_now
);
309 uint64_t cc_seconds
=CYCLE_TIMER_GET_SECS(ctr_now
);
311 // check for bogus ctr
312 // the cycle timer should be ahead of the receive timer
313 int diff_cycles
= diffCycles(cc_cycles
, rcv_cycle
);
315 debugWarning("current cycle timer not ahead of receive cycle: rcv: %u / cc: %"PRIu64
" (%d)\n",
316 rcv_cycle
, cc_cycles
, diff_cycles
);
319 // the cycletimer has wrapped since this packet was received
320 // we want cc_seconds to reflect the 'seconds' at the point this
322 if (rcv_cycle
>cc_cycles
&& (diff_cycles
>=0)) {
326 // seconds has wrapped around, so we'd better not substract 1
327 // the good value is 127
332 // reconstruct the top part of the timestamp using the current cycle number
333 uint64_t rcv_cycle_masked
=rcv_cycle
& 0xF;
334 uint64_t syt_cycle
=CYCLE_TIMER_GET_CYCLES(syt_timestamp
);
336 // if this is true, wraparound has occurred, undo this wraparound
337 if(syt_cycle
<rcv_cycle_masked
) syt_cycle
+= 0x10;
339 // this is the difference in cycles wrt the cycle the
340 // timestamp was received
341 uint64_t delta_cycles
=syt_cycle
-rcv_cycle_masked
;
343 // reconstruct the cycle part of the timestamp
344 uint64_t new_cycles
=rcv_cycle
+ delta_cycles
;
346 // if the cycles cause a wraparound of the cycle timer,
347 // perform this wraparound
348 // and convert the timestamp into ticks
349 if(new_cycles
<8000) {
350 timestamp
= new_cycles
* TICKS_PER_CYCLE
;
352 debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE
,
353 "Detected wraparound: %u + %"PRId64
" = %"PRId64
"\n",
354 rcv_cycle
, delta_cycles
, new_cycles
);
356 new_cycles
-=8000; // wrap around
358 if (new_cycles
>= 8000) {
359 debugWarning("insufficient unwrapping\n");
362 timestamp
= new_cycles
* TICKS_PER_CYCLE
;
363 // add one second due to wraparound
364 timestamp
+= TICKS_PER_SECOND
;
367 timestamp
+= CYCLE_TIMER_GET_OFFSET(syt_timestamp
);
369 timestamp
= addTicks(timestamp
, cc_seconds
* TICKS_PER_SECOND
);
372 if(( TICKS_TO_CYCLE_TIMER(timestamp
) & 0xFFFF) != syt_timestamp
) {
373 debugWarning("back-converted timestamp not equal to SYT\n");
374 debugWarning("TS=%011"PRIu64
" TSC=%08"PRIX64
" SYT=%04"PRIX64
"\n",
375 timestamp
, TICKS_TO_CYCLE_TIMER(timestamp
), syt_timestamp
);
383 * @brief Converts a received SYT timestamp to a full timestamp in ticks.
386 * @param syt_timestamp The SYT timestamp as present in the packet
387 * @param rcv_ctr The CTR value this timestamp was received on (offset can be 0)
390 static inline uint64_t sytRecvToFullTicks2(uint64_t syt_timestamp
, uint32_t rcv_ctr
) {
393 debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE
, "SYT=%04"PRIX64
" RCV_CTR=%08X\n",
394 syt_timestamp
, rcv_ctr
);
396 // reconstruct the top part of the timestamp using the current cycle number
397 unsigned int rcv_cycle
= CYCLE_TIMER_GET_CYCLES(rcv_ctr
);
398 unsigned int rcv_cycle_masked
= rcv_cycle
& 0xF;
399 unsigned int syt_cycle
= CYCLE_TIMER_GET_CYCLES(syt_timestamp
);
401 // if this is true, wraparound has occurred, undo this wraparound
402 if(syt_cycle
<rcv_cycle_masked
) syt_cycle
+= 0x10;
404 // this is the difference in cycles wrt the cycle the
405 // timestamp was received
406 unsigned int delta_cycles
= syt_cycle
- rcv_cycle_masked
;
408 // reconstruct the cycle part of the timestamp
409 rcv_cycle
+= delta_cycles
;
411 // if the cycles cause a wraparound of the cycle timer,
412 // perform this wraparound
413 // and convert the timestamp into ticks
415 timestamp
= rcv_cycle
* TICKS_PER_CYCLE
;
417 rcv_cycle
-= 8000; // wrap around
419 if (rcv_cycle
>= 8000) {
420 debugWarning("insufficient unwrapping\n");
423 timestamp
= rcv_cycle
* TICKS_PER_CYCLE
;
424 // add one second due to wraparound
425 timestamp
+= TICKS_PER_SECOND
;
428 timestamp
+= CYCLE_TIMER_GET_OFFSET(syt_timestamp
);
430 timestamp
= addTicks(timestamp
, CYCLE_TIMER_GET_SECS(rcv_ctr
) * TICKS_PER_SECOND
);
433 if(( TICKS_TO_CYCLE_TIMER(timestamp
) & 0xFFFF) != syt_timestamp
) {
434 debugWarning("back-converted timestamp not equal to SYT\n");
435 debugWarning("TS=%011"PRIu64
" TSC=%08"PRIX64
" SYT=%04"PRIX64
"\n",
436 timestamp
, TICKS_TO_CYCLE_TIMER(timestamp
), syt_timestamp
);
444 * @brief Converts a transmit SYT timestamp to a full timestamp in ticks.
446 * The difference between sytRecvToFullTicks and sytXmitToFullTicks is
447 * the way SYT cycle wraparound is detected: in the receive version,
448 * wraparound is present if rcv_cycle > current_cycle. In the xmit
449 * version this is when current_cycle > xmt_cycle.
451 * @param syt_timestamp The SYT timestamp as present in the packet
452 * @param xmt_cycle The cycle this timestamp was received on
453 * @param ctr_now The current value of the cycle timer ('now')
456 static inline uint64_t sytXmitToFullTicks(uint64_t syt_timestamp
, unsigned int xmt_cycle
, uint64_t ctr_now
) {
459 debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE
, "SYT=%08"PRIX64
" CY=%04X CTR=%08"PRIX64
"\n",
460 syt_timestamp
, xmt_cycle
, ctr_now
);
462 // reconstruct the full cycle
463 uint64_t cc_cycles
=CYCLE_TIMER_GET_CYCLES(ctr_now
);
464 uint64_t cc_seconds
=CYCLE_TIMER_GET_SECS(ctr_now
);
466 // check for bogus CTR
467 int diff_cycles
= diffCycles(xmt_cycle
, cc_cycles
);
469 debugWarning("xmit cycle not ahead of current cycle: xmt: %u / cc: %"PRIu64
" (%d)\n",
470 xmt_cycle
, cc_cycles
, diff_cycles
);
473 // the cycletimer has wrapped since this packet was received
474 // we want cc_seconds to reflect the 'seconds' at the point this
475 // is to be transmitted
476 if (cc_cycles
>xmt_cycle
&& (diff_cycles
>=0)) {
480 // seconds has wrapped around, so we'd better not substract 1
481 // the good value is 127
486 // reconstruct the top part of the timestamp using the current cycle number
487 uint64_t xmt_cycle_masked
=xmt_cycle
& 0xF;
488 uint64_t syt_cycle
=CYCLE_TIMER_GET_CYCLES(syt_timestamp
);
490 // if this is true, wraparound has occurred, undo this wraparound
491 if(syt_cycle
<xmt_cycle_masked
) syt_cycle
+= 0x10;
493 // this is the difference in cycles wrt the cycle the
494 // timestamp was received
495 uint64_t delta_cycles
=syt_cycle
-xmt_cycle_masked
;
497 // reconstruct the cycle part of the timestamp
498 uint64_t new_cycles
=xmt_cycle
+ delta_cycles
;
500 // if the cycles cause a wraparound of the cycle timer,
501 // perform this wraparound
502 // and convert the timestamp into ticks
503 if(new_cycles
<8000) {
504 timestamp
= new_cycles
* TICKS_PER_CYCLE
;
506 debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE
,
507 "Detected wraparound: %u + %"PRId64
" = %"PRId64
"\n",
508 xmt_cycle
, delta_cycles
, new_cycles
);
510 new_cycles
-=8000; // wrap around
512 if (new_cycles
>= 8000) {
513 debugWarning("insufficient unwrapping\n");
516 timestamp
= new_cycles
* TICKS_PER_CYCLE
;
517 // add one second due to wraparound
518 timestamp
+= TICKS_PER_SECOND
;
521 timestamp
+= CYCLE_TIMER_GET_OFFSET(syt_timestamp
);
523 timestamp
= addTicks(timestamp
, cc_seconds
* TICKS_PER_SECOND
);
526 if(( TICKS_TO_CYCLE_TIMER(timestamp
) & 0xFFFF) != syt_timestamp
) {
527 debugWarning("back-converted timestamp not equal to SYT\n");
528 debugWarning("TS=%011"PRIu64
" TSC=%08"PRIX64
" SYT=%04"PRIX64
"\n",
529 timestamp
, TICKS_TO_CYCLE_TIMER(timestamp
), syt_timestamp
);
536 #endif // __CYCLETIMER_H__