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

Revision 394, 11.9 kB (checked in by pieterpalmers, 17 years ago)

- fixed SYT timestamp to ticks conversion

Line 
1 /* $Id$ */
2
3 /*
4  *   FreeBob Streaming API
5  *   FreeBob = Firewire (pro-)audio for linux
6  *
7  *   http://freebob.sf.net
8  *
9  *   Copyright (C) 2005,2006,2007 Pieter Palmers <pieterpalmers@users.sourceforge.net>
10  *
11  *   This program is free software {} you can redistribute it and/or modify
12  *   it under the terms of the GNU General Public License as published by
13  *   the Free Software Foundation {} either version 2 of the License, or
14  *   (at your option) any later version.
15  *
16  *   This program is distributed in the hope that it will be useful,
17  *   but WITHOUT ANY WARRANTY {} without even the implied warranty of
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *   GNU General Public License for more details.
20  *
21  *   You should have received a copy of the GNU General Public License
22  *   along with this program {} if not, write to the Free Software
23  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  *
25  *
26  *
27  */
28  
29 /* Definitions and utility macro's to handle the ISO cycle timer */
30
31 #ifndef __CYCLETIMER_H__
32 #define __CYCLETIMER_H__
33
34 #include <inttypes.h>
35 #include "debugmodule/debugmodule.h"
36
37 #define CSR_CYCLE_TIME            0x200
38 #define CSR_REGISTER_BASE  0xfffff0000000ULL
39
40 #define CYCLES_PER_SECOND   8000U
41 #define TICKS_PER_CYCLE     3072U
42 #define TICKS_PER_SECOND    24576000UL
43 #define TICKS_PER_USEC     (24.576000)
44
45 #define USECS_PER_TICK     (1.0/TICKS_PER_USEC)
46 #define USECS_PER_CYCLE    (125U)
47
48 #define CYCLE_TIMER_GET_SECS(x)   ((((x) & 0xFE000000U) >> 25))
49 #define CYCLE_TIMER_GET_CYCLES(x) ((((x) & 0x01FFF000U) >> 12))
50 #define CYCLE_TIMER_GET_OFFSET(x)  ((((x) & 0x00000FFFU)))
51 #define CYCLE_TIMER_TO_TICKS(x) ((CYCLE_TIMER_GET_SECS(x)   * TICKS_PER_SECOND) +\
52                                    (CYCLE_TIMER_GET_CYCLES(x) * TICKS_PER_CYCLE ) +\
53                                    (CYCLE_TIMER_GET_OFFSET(x)            ))
54                                    
55 // non-efficient versions, to be avoided in critical code
56 #define TICKS_TO_SECS(x) ((x)/TICKS_PER_SECOND)
57 #define TICKS_TO_CYCLES(x) (((x)/TICKS_PER_CYCLE) % CYCLES_PER_SECOND)
58 #define TICKS_TO_OFFSET(x) (((x)%TICKS_PER_CYCLE))
59
60 #define TICKS_TO_CYCLE_TIMER(x) (  ((TICKS_TO_SECS(x) & 0x7F) << 25) \
61                                  | ((TICKS_TO_CYCLES(x) & 0x1FFF) << 12) \
62                                  | ((TICKS_TO_OFFSET(x) & 0xFFF)))
63                                  
64 #define TICKS_TO_SYT(x)         (((TICKS_TO_CYCLES(x) & 0xF) << 12) \
65                                  | ((TICKS_TO_OFFSET(x) & 0xFFF)))
66
67 #define CYCLE_TIMER_UNWRAP_TICKS(x) (((uint64_t)(x)) \
68                                        + (127ULL * TICKS_PER_SECOND) \
69                                        + (CYCLES_PER_SECOND * TICKS_PER_CYCLE) \
70                                        + (TICKS_PER_CYCLE) \
71                                       )
72 #define CYCLE_TIMER_WRAP_TICKS(x) ((x % TICKS_PER_SECOND))
73
74 DECLARE_GLOBAL_DEBUG_MODULE;
75
76 /**
77  * @brief Converts a received SYT timestamp to a full timestamp in ticks.
78  *
79  *
80  * @param syt_timestamp The SYT timestamp as present in the packet
81  * @param rcv_cycle The cycle this timestamp was received on
82  * @param ctr_now The current value of the cycle timer ('now')
83  * @return
84  */
85 static inline uint32_t sytRecvToFullTicks(uint32_t syt_timestamp, unsigned int rcv_cycle, uint32_t ctr_now) {
86     uint32_t timestamp;
87    
88     debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"SYT=%08X CY=%04X CTR=%08X\n",
89         syt_timestamp,rcv_cycle,ctr_now);
90        
91     // reconstruct the full cycle
92     uint32_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now);
93     uint32_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now);
94    
95     // the cycletimer has wrapped since this packet was received
96     // we want cc_seconds to reflect the 'seconds' at the point this
97     // was received
98     if (rcv_cycle>cc_cycles) {
99         if (cc_seconds) {
100             cc_seconds--;
101         } else {
102             // seconds has wrapped around, so we'd better not substract 1
103             // the good value is 127
104             cc_seconds=127;
105         }
106     }
107    
108     // reconstruct the top part of the timestamp using the current cycle number
109     uint32_t rcv_cycle_masked=rcv_cycle & 0xF;
110     uint32_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp);
111    
112     // if this is true, wraparound has occurred, undo this wraparound
113     if(syt_cycle<rcv_cycle_masked) syt_cycle += 0x10;
114    
115     // this is the difference in cycles wrt the cycle the
116     // timestamp was received
117     uint32_t delta_cycles=syt_cycle-rcv_cycle_masked;
118    
119     // reconstruct the cycle part of the timestamp
120     uint32_t new_cycles=rcv_cycle + delta_cycles;
121    
122     // if the cycles cause a wraparound of the cycle timer,
123     // perform this wraparound
124     // and convert the timestamp into ticks
125     if(new_cycles<8000) {
126         timestamp  = new_cycles * TICKS_PER_CYCLE;
127     } else {
128         debugOutput(DEBUG_LEVEL_VERY_VERBOSE,
129             "Detected wraparound: %d + %d = %d\n",
130             rcv_cycle,delta_cycles,new_cycles);
131        
132         new_cycles-=8000; // wrap around
133 #ifdef DEBUG
134         if (new_cycles >= 8000) {
135             debugWarning("insufficient unwrapping\n");
136         }
137 #endif
138         timestamp  = new_cycles * TICKS_PER_CYCLE;
139         // add one second due to wraparound
140         timestamp += TICKS_PER_SECOND;
141     }
142    
143     timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp);
144    
145     timestamp += cc_seconds * TICKS_PER_SECOND;
146    
147     #ifdef DEBUG
148         if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) {
149             debugWarning("back-converted timestamp not equal to SYT\n");
150             debugWarning("TS=%011llu TSC=%08X SYT=%04X\n",
151                   timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp);
152         }
153     #endif
154    
155     return timestamp;
156 }
157
158 /**
159  * @brief Converts a transmit SYT timestamp to a full timestamp in ticks.
160  *
161  * The difference between sytRecvToFullTicks and sytXmitToFullTicks is
162  * the way SYT cycle wraparound is detected: in the receive version,
163  * wraparound is present if rcv_cycle > current_cycle. In the xmit
164  * version this is when current_cycle > xmt_cycle.
165  *
166  * @param syt_timestamp The SYT timestamp as present in the packet
167  * @param xmt_cycle The cycle this timestamp was received on
168  * @param ctr_now The current value of the cycle timer ('now')
169  * @return
170  */
171 static inline uint32_t sytXmitToFullTicks(uint32_t syt_timestamp, unsigned int xmt_cycle, uint32_t ctr_now) {
172     uint32_t timestamp;
173    
174     debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"SYT=%08X CY=%04X CTR=%08X\n",
175         syt_timestamp,xmt_cycle,ctr_now);
176        
177     // reconstruct the full cycle
178     uint32_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now);
179     uint32_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now);
180    
181     // the cycletimer has wrapped since this packet was received
182     // we want cc_seconds to reflect the 'seconds' at the point this
183     // is to be transmitted
184     if (xmt_cycle<cc_cycles) {
185         if (cc_seconds) {
186             cc_seconds--;
187         } else {
188             // seconds has wrapped around, so we'd better not substract 1
189             // the good value is 127
190             cc_seconds=127;
191         }
192     }
193    
194     // reconstruct the top part of the timestamp using the current cycle number
195     uint32_t xmt_cycle_masked=xmt_cycle & 0xF;
196     uint32_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp);
197    
198     // if this is true, wraparound has occurred, undo this wraparound
199     if(syt_cycle<xmt_cycle_masked) syt_cycle += 0x10;
200    
201     // this is the difference in cycles wrt the cycle the
202     // timestamp was received
203     uint32_t delta_cycles=syt_cycle-xmt_cycle_masked;
204    
205     // reconstruct the cycle part of the timestamp
206     uint32_t new_cycles=xmt_cycle + delta_cycles;
207    
208     // if the cycles cause a wraparound of the cycle timer,
209     // perform this wraparound
210     // and convert the timestamp into ticks
211     if(new_cycles<8000) {
212         timestamp  = new_cycles * TICKS_PER_CYCLE;
213     } else {
214         debugOutput(DEBUG_LEVEL_VERY_VERBOSE,
215             "Detected wraparound: %d + %d = %d\n",
216             xmt_cycle,delta_cycles,new_cycles);
217        
218         new_cycles-=8000; // wrap around
219 #ifdef DEBUG
220         if (new_cycles >= 8000) {
221             debugWarning("insufficient unwrapping\n");
222         }
223 #endif
224         timestamp  = new_cycles * TICKS_PER_CYCLE;
225         // add one second due to wraparound
226         timestamp += TICKS_PER_SECOND;
227     }
228    
229     timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp);
230    
231     timestamp += cc_seconds * TICKS_PER_SECOND;
232    
233     #ifdef DEBUG
234         if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) {
235             debugWarning("back-converted timestamp not equal to SYT\n");
236             debugWarning("TS=%011llu TSC=%08X SYT=%04X\n",
237                   timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp);
238         }
239     #endif
240    
241     return timestamp;
242 }
243
244 /**
245  * @brief Wraps x to the maximum number of ticks
246  *
247  * The input value is wrapped to the maximum value of the cycle
248  * timer, in ticks (128sec * 24576000 ticks/sec).
249  *
250  * @param x time to wrap
251  * @return wrapped time
252  */
253 static inline uint32_t wrapAtMaxTicks(uint64_t x) {
254     if (x >= TICKS_PER_SECOND * 128L) {
255         x -= TICKS_PER_SECOND * 128L;
256     }
257
258 #ifdef DEBUG
259         if (x >= TICKS_PER_SECOND * 128L) {
260             debugWarning("insufficient wrapping: %llu\n",x);
261         }
262 #endif
263
264     return x;
265 }
266
267 /**
268  * @brief Wraps both at minimum and maximum value for ticks
269  * @param x value to wrap
270  * @return wrapped value
271  */
272 static inline uint32_t wrapAtMinMaxTicks(int64_t x) {
273    
274     if (x < 0) {
275         x += TICKS_PER_SECOND * 128L;
276     } else if (x >= TICKS_PER_SECOND * 128L) {
277         x -= TICKS_PER_SECOND * 128L;
278     }
279
280 #ifdef DEBUG
281         if (x >= TICKS_PER_SECOND * 128L) {
282             debugWarning("insufficient wrapping (max): %llu\n",x);
283         }
284         if (x < 0) {
285             debugWarning("insufficient wrapping (min): %lld\n",x);
286         }
287 #endif
288     return x;
289
290 }
291
292 /**
293  * @brief Computes a difference between timestamps
294  *
295  * This function computes a difference between timestamps
296  * such that it respects wrapping.
297  *
298  * If x wraps around, but y doesn't, the result of x-y is
299  * negative and very large. However the real difference is
300  * not large. It can be calculated by unwrapping x and then
301  * calculating x-y.
302  *
303  * @param x First timestamp
304  * @param y Second timestamp
305  * @return the difference x-y, unwrapped
306  */
307 static inline int32_t substractTicks(uint32_t x, uint32_t y) {
308     int64_t diff=(int64_t)x - (int64_t)y;
309    
310     // the maximal difference we allow (64secs)
311     const int64_t max=TICKS_PER_SECOND*64L;
312    
313     if(diff > max) {
314         // this means that y has wrapped, but
315         // x has not. we should unwrap y
316         // by adding TICKS_PER_SECOND*128L, meaning that we should substract
317         // this value from diff
318         diff -= TICKS_PER_SECOND*128L;
319     } else if (diff < -max) {
320         // this means that x has wrapped, but
321         // y has not. we should unwrap x
322         // by adding TICKS_PER_SECOND*128L, meaning that we should add
323         // this value to diff
324         diff += TICKS_PER_SECOND*128L;
325     }
326    
327     return (int32_t)diff;
328    
329 }
330
331 /**
332  * @brief Computes a sum of timestamps
333  *
334  * This function computes a sum of timestamps in ticks,
335  * wrapping the result if nescessary.
336  *
337  * @param x First timestamp
338  * @param y Second timestamp
339  * @return the sum x+y, wrapped
340  */
341 static inline uint32_t addTicks(uint32_t x, uint32_t y) {
342     uint64_t sum=x+y;
343
344     return wrapAtMaxTicks(sum);
345 }
346
347 /**
348  * @brief Computes a difference between cycles
349  *
350  * This function computes a difference between cycles
351  * such that it respects wrapping (at 8000 cycles).
352  *
353  * See substractTicks
354  *
355  * @param x First cycle value
356  * @param y Second cycle value
357  * @return the difference x-y, unwrapped
358  */
359 static inline int substractCycles(unsigned int x, unsigned int y) {
360     int diff = x - y;
361    
362     // the maximal difference we allow (64secs)
363     const int max=CYCLES_PER_SECOND/2;
364    
365     if(diff > max) {
366         diff -= CYCLES_PER_SECOND;
367     } else if (diff < -max) {
368         diff += CYCLES_PER_SECOND;
369     }
370    
371     return diff;
372 }
373
374 #endif // __CYCLETIMER_H__
Note: See TracBrowser for help on using the browser.