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

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

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.