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

Revision 397, 12.0 kB (checked in by pieterpalmers, 17 years ago)

- make timestampedbuffer use floats instead of doubles
- change iso receive back to the efficient case

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) & 0xFE000000UL) >> 25))
49 #define CYCLE_TIMER_GET_CYCLES(x) ((((x) & 0x01FFF000UL) >> 12))
50 #define CYCLE_TIMER_GET_OFFSET(x)  ((((x) & 0x00000FFFUL)))
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 Wraps x to the maximum number of ticks
78  *
79  * The input value is wrapped to the maximum value of the cycle
80  * timer, in ticks (128sec * 24576000 ticks/sec).
81  *
82  * @param x time to wrap
83  * @return wrapped time
84  */
85 static inline uint32_t wrapAtMaxTicks(uint64_t x) {
86     if (x >= TICKS_PER_SECOND * 128L) {
87         x -= TICKS_PER_SECOND * 128L;
88     }
89
90 #ifdef DEBUG
91         if (x >= TICKS_PER_SECOND * 128L) {
92             debugWarning("insufficient wrapping: %llu\n",x);
93         }
94 #endif
95
96     return x;
97 }
98
99 /**
100  * @brief Wraps both at minimum and maximum value for ticks
101  * @param x value to wrap
102  * @return wrapped value
103  */
104 static inline uint32_t wrapAtMinMaxTicks(int64_t x) {
105    
106     if (x < 0) {
107         x += TICKS_PER_SECOND * 128L;
108     } else if (x >= TICKS_PER_SECOND * 128L) {
109         x -= TICKS_PER_SECOND * 128L;
110     }
111
112 #ifdef DEBUG
113         if (x >= TICKS_PER_SECOND * 128L) {
114             debugWarning("insufficient wrapping (max): %llu\n",x);
115         }
116         if (x < 0) {
117             debugWarning("insufficient wrapping (min): %lld\n",x);
118         }
119 #endif
120     return x;
121
122 }
123
124 /**
125  * @brief Computes a difference between timestamps
126  *
127  * This function computes a difference between timestamps
128  * such that it respects wrapping.
129  *
130  * If x wraps around, but y doesn't, the result of x-y is
131  * negative and very large. However the real difference is
132  * not large. It can be calculated by unwrapping x and then
133  * calculating x-y.
134  *
135  * @param x First timestamp
136  * @param y Second timestamp
137  * @return the difference x-y, unwrapped
138  */
139 static inline int32_t substractTicks(uint32_t x, uint32_t y) {
140     int64_t diff=(int64_t)x - (int64_t)y;
141    
142     // the maximal difference we allow (64secs)
143     const int64_t max=TICKS_PER_SECOND*64L;
144    
145     if(diff > max) {
146         // this means that y has wrapped, but
147         // x has not. we should unwrap y
148         // by adding TICKS_PER_SECOND*128L, meaning that we should substract
149         // this value from diff
150         diff -= TICKS_PER_SECOND*128L;
151     } else if (diff < -max) {
152         // this means that x has wrapped, but
153         // y has not. we should unwrap x
154         // by adding TICKS_PER_SECOND*128L, meaning that we should add
155         // this value to diff
156         diff += TICKS_PER_SECOND*128L;
157     }
158    
159     return (int32_t)diff;
160    
161 }
162
163 /**
164  * @brief Computes a sum of timestamps
165  *
166  * This function computes a sum of timestamps in ticks,
167  * wrapping the result if nescessary.
168  *
169  * @param x First timestamp
170  * @param y Second timestamp
171  * @return the sum x+y, wrapped
172  */
173 static inline uint32_t addTicks(uint32_t x, uint32_t y) {
174     uint64_t sum=x+y;
175
176     return wrapAtMaxTicks(sum);
177 }
178
179 /**
180  * @brief Converts a received SYT timestamp to a full timestamp in ticks.
181  *
182  *
183  * @param syt_timestamp The SYT timestamp as present in the packet
184  * @param rcv_cycle The cycle this timestamp was received on
185  * @param ctr_now The current value of the cycle timer ('now')
186  * @return
187  */
188 static inline uint32_t sytRecvToFullTicks(uint32_t syt_timestamp, unsigned int rcv_cycle, uint32_t ctr_now) {
189     uint32_t timestamp;
190    
191     debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"SYT=%08X CY=%04X CTR=%08X\n",
192         syt_timestamp,rcv_cycle,ctr_now);
193        
194     // reconstruct the full cycle
195     uint32_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now);
196     uint32_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now);
197    
198     // the cycletimer has wrapped since this packet was received
199     // we want cc_seconds to reflect the 'seconds' at the point this
200     // was received
201     if (rcv_cycle>cc_cycles) {
202         if (cc_seconds) {
203             cc_seconds--;
204         } else {
205             // seconds has wrapped around, so we'd better not substract 1
206             // the good value is 127
207             cc_seconds=127;
208         }
209     }
210    
211     // reconstruct the top part of the timestamp using the current cycle number
212     uint32_t rcv_cycle_masked=rcv_cycle & 0xF;
213     uint32_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp);
214    
215     // if this is true, wraparound has occurred, undo this wraparound
216     if(syt_cycle<rcv_cycle_masked) syt_cycle += 0x10;
217    
218     // this is the difference in cycles wrt the cycle the
219     // timestamp was received
220     uint32_t delta_cycles=syt_cycle-rcv_cycle_masked;
221    
222     // reconstruct the cycle part of the timestamp
223     uint32_t new_cycles=rcv_cycle + delta_cycles;
224    
225     // if the cycles cause a wraparound of the cycle timer,
226     // perform this wraparound
227     // and convert the timestamp into ticks
228     if(new_cycles<8000) {
229         timestamp  = new_cycles * TICKS_PER_CYCLE;
230     } else {
231         debugOutput(DEBUG_LEVEL_VERY_VERBOSE,
232             "Detected wraparound: %d + %d = %d\n",
233             rcv_cycle,delta_cycles,new_cycles);
234        
235         new_cycles-=8000; // wrap around
236 #ifdef DEBUG
237         if (new_cycles >= 8000) {
238             debugWarning("insufficient unwrapping\n");
239         }
240 #endif
241         timestamp  = new_cycles * TICKS_PER_CYCLE;
242         // add one second due to wraparound
243         timestamp += TICKS_PER_SECOND;
244     }
245    
246     timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp);
247    
248     timestamp = addTicks(timestamp, cc_seconds * TICKS_PER_SECOND);
249    
250     #ifdef DEBUG
251         if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) {
252             debugWarning("back-converted timestamp not equal to SYT\n");
253             debugWarning("TS=%011llu TSC=%08X SYT=%04X\n",
254                   timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp);
255         }
256     #endif
257    
258     return timestamp;
259 }
260
261 /**
262  * @brief Converts a transmit SYT timestamp to a full timestamp in ticks.
263  *
264  * The difference between sytRecvToFullTicks and sytXmitToFullTicks is
265  * the way SYT cycle wraparound is detected: in the receive version,
266  * wraparound is present if rcv_cycle > current_cycle. In the xmit
267  * version this is when current_cycle > xmt_cycle.
268  *
269  * @param syt_timestamp The SYT timestamp as present in the packet
270  * @param xmt_cycle The cycle this timestamp was received on
271  * @param ctr_now The current value of the cycle timer ('now')
272  * @return
273  */
274 static inline uint32_t sytXmitToFullTicks(uint32_t syt_timestamp, unsigned int xmt_cycle, uint32_t ctr_now) {
275     uint32_t timestamp;
276    
277     debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"SYT=%08X CY=%04X CTR=%08X\n",
278         syt_timestamp,xmt_cycle,ctr_now);
279        
280     // reconstruct the full cycle
281     uint32_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now);
282     uint32_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now);
283    
284     // the cycletimer has wrapped since this packet was received
285     // we want cc_seconds to reflect the 'seconds' at the point this
286     // is to be transmitted
287     if (xmt_cycle<cc_cycles) {
288         if (cc_seconds) {
289             cc_seconds--;
290         } else {
291             // seconds has wrapped around, so we'd better not substract 1
292             // the good value is 127
293             cc_seconds=127;
294         }
295     }
296    
297     // reconstruct the top part of the timestamp using the current cycle number
298     uint32_t xmt_cycle_masked=xmt_cycle & 0xF;
299     uint32_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp);
300    
301     // if this is true, wraparound has occurred, undo this wraparound
302     if(syt_cycle<xmt_cycle_masked) syt_cycle += 0x10;
303    
304     // this is the difference in cycles wrt the cycle the
305     // timestamp was received
306     uint32_t delta_cycles=syt_cycle-xmt_cycle_masked;
307    
308     // reconstruct the cycle part of the timestamp
309     uint32_t new_cycles=xmt_cycle + delta_cycles;
310    
311     // if the cycles cause a wraparound of the cycle timer,
312     // perform this wraparound
313     // and convert the timestamp into ticks
314     if(new_cycles<8000) {
315         timestamp  = new_cycles * TICKS_PER_CYCLE;
316     } else {
317         debugOutput(DEBUG_LEVEL_VERY_VERBOSE,
318             "Detected wraparound: %d + %d = %d\n",
319             xmt_cycle,delta_cycles,new_cycles);
320        
321         new_cycles-=8000; // wrap around
322 #ifdef DEBUG
323         if (new_cycles >= 8000) {
324             debugWarning("insufficient unwrapping\n");
325         }
326 #endif
327         timestamp  = new_cycles * TICKS_PER_CYCLE;
328         // add one second due to wraparound
329         timestamp += TICKS_PER_SECOND;
330     }
331    
332     timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp);
333    
334     timestamp = addTicks(timestamp, cc_seconds * TICKS_PER_SECOND);
335    
336     #ifdef DEBUG
337         if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) {
338             debugWarning("back-converted timestamp not equal to SYT\n");
339             debugWarning("TS=%011llu TSC=%08X SYT=%04X\n",
340                   timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp);
341         }
342     #endif
343    
344     return timestamp;
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.