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

Revision 707, 13.2 kB (checked in by ppalmers, 15 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 "debugmodule/debugmodule.h" |

30 | |

31 | #include <inttypes.h> |

32 | |

33 | #define CSR_CYCLE_TIME 0x200 |

34 | #define CSR_REGISTER_BASE 0xfffff0000000ULL |

35 | |

36 | #define CYCLES_PER_SECOND 8000U |

37 | #define TICKS_PER_CYCLE 3072U |

38 | #define TICKS_PER_SECOND 24576000UL |

39 | #define TICKS_PER_USEC (24.576000) |

40 | |

41 | #define USECS_PER_TICK (1.0/TICKS_PER_USEC) |

42 | #define USECS_PER_CYCLE (125U) |

43 | |

44 | #define CYCLE_TIMER_GET_SECS(x) ((((x) & 0xFE000000UL) >> 25)) |

45 | #define CYCLE_TIMER_GET_CYCLES(x) ((((x) & 0x01FFF000UL) >> 12)) |

46 | #define CYCLE_TIMER_GET_OFFSET(x) ((((x) & 0x00000FFFUL))) |

47 | #define CYCLE_TIMER_TO_TICKS(x) ((CYCLE_TIMER_GET_SECS(x) * TICKS_PER_SECOND) +\ |

48 | (CYCLE_TIMER_GET_CYCLES(x) * TICKS_PER_CYCLE ) +\ |

49 | (CYCLE_TIMER_GET_OFFSET(x) )) |

50 | |

51 | // non-efficient versions, to be avoided in critical code |

52 | #define TICKS_TO_SECS(x) ((x)/TICKS_PER_SECOND) |

53 | #define TICKS_TO_CYCLES(x) (((x)/TICKS_PER_CYCLE) % CYCLES_PER_SECOND) |

54 | #define TICKS_TO_OFFSET(x) (((x)%TICKS_PER_CYCLE)) |

55 | |

56 | #define TICKS_TO_CYCLE_TIMER(x) ( ((TICKS_TO_SECS(x) & 0x7F) << 25) \ |

57 | | ((TICKS_TO_CYCLES(x) & 0x1FFF) << 12) \ |

58 | | ((TICKS_TO_OFFSET(x) & 0xFFF))) |

59 | |

60 | #define TICKS_TO_SYT(x) (((TICKS_TO_CYCLES(x) & 0xF) << 12) \ |

61 | | ((TICKS_TO_OFFSET(x) & 0xFFF))) |

62 | |

63 | #define CYCLE_TIMER_UNWRAP_TICKS(x) (((uint64_t)(x)) \ |

64 | + (127ULL * TICKS_PER_SECOND) \ |

65 | + (CYCLES_PER_SECOND * TICKS_PER_CYCLE) \ |

66 | + (TICKS_PER_CYCLE) \ |

67 | ) |

68 | #define CYCLE_TIMER_WRAP_TICKS(x) ((x % TICKS_PER_SECOND)) |

69 | |

70 | #define INVALID_TIMESTAMP_TICKS 0xFFFFFFFFFFFFFFFFULL |

71 | |

72 | DECLARE_GLOBAL_DEBUG_MODULE; |

73 | |

74 | /** |

75 | * @brief Wraps x to the maximum number of ticks |

76 | * |

77 | * The input value is wrapped to the maximum value of the cycle |

78 | * timer, in ticks (128sec * 24576000 ticks/sec). |

79 | * |

80 | * @param x time to wrap |

81 | * @return wrapped time |

82 | */ |

83 | static inline uint64_t wrapAtMaxTicks(uint64_t x) { |

84 | if (x >= TICKS_PER_SECOND * 128L) { |

85 | x -= TICKS_PER_SECOND * 128L; |

86 | } |

87 | |

88 | #ifdef DEBUG |

89 | if (x >= TICKS_PER_SECOND * 128L) { |

90 | debugWarning("insufficient wrapping: %llu\n",x); |

91 | } |

92 | #endif |

93 | |

94 | return x; |

95 | } |

96 | |

97 | /** |

98 | * @brief Wraps x to the minimum number of ticks |

99 | * |

100 | * The input value is wrapped to the minimum value of the cycle |

101 | * timer, in ticks (= 0). |

102 | * |

103 | * @param x time to wrap |

104 | * @return wrapped time |

105 | */ |

106 | static inline int64_t wrapAtMinTicks(int64_t x) { |

107 | if (x < 0) { |

108 | x += TICKS_PER_SECOND * 128L; |

109 | } |

110 | |

111 | #ifdef DEBUG |

112 | if (x < 0) { |

113 | debugWarning("insufficient wrapping: %lld\n",x); |

114 | |

115 | debugWarning("correcting...\n"); |

116 | while (x < 0) { |

117 | x += TICKS_PER_SECOND * 128L; |

118 | |

119 | if (x < 0) { |

120 | debugWarning(" insufficient wrapping: %lld\n",x); |

121 | } |

122 | } |

123 | } |

124 | |

125 | #endif |

126 | |

127 | return (int64_t)x; |

128 | } |

129 | |

130 | /** |

131 | * @brief Wraps both at minimum and maximum value for ticks |

132 | * |

133 | * The input value is wrapped to the maximum value of the cycle |

134 | * timer, in ticks (128sec * 24576000 ticks/sec), and |

135 | * to the minimum value of the cycle timer, in ticks (= 0). |

136 | * |

137 | * @param x value to wrap |

138 | * @return wrapped value |

139 | */ |

140 | static inline int64_t wrapAtMinMaxTicks(int64_t x) { |

141 | |

142 | if (x < 0) { |

143 | x += TICKS_PER_SECOND * 128L; |

144 | } else if (x >= TICKS_PER_SECOND * 128L) { |

145 | x -= TICKS_PER_SECOND * 128L; |

146 | } |

147 | |

148 | #ifdef DEBUG |

149 | if (x >= TICKS_PER_SECOND * 128L) { |

150 | debugWarning("insufficient wrapping (max): %llu\n",x); |

151 | } |

152 | if (x < 0) { |

153 | debugWarning("insufficient wrapping (min): %lld\n",x); |

154 | } |

155 | #endif |

156 | return x; |

157 | |

158 | } |

159 | |

160 | /** |

161 | * @brief Computes a difference between cycles |

162 | * |

163 | * This function computes a difference between cycles |

164 | * such that it respects wrapping (at 8000 cycles). |

165 | * |

166 | * See diffTicks |

167 | * |

168 | * @param x First cycle value |

169 | * @param y Second cycle value |

170 | * @return the difference x-y, unwrapped |

171 | */ |

172 | static inline int diffCycles(unsigned int x, unsigned int y) { |

173 | int diff = x - y; |

174 | |

175 | // the maximal difference we allow (64secs) |

176 | const int max=CYCLES_PER_SECOND/2; |

177 | |

178 | if(diff > max) { |

179 | diff -= CYCLES_PER_SECOND; |

180 | } else if (diff < -max) { |

181 | diff += CYCLES_PER_SECOND; |

182 | } |

183 | |

184 | return diff; |

185 | } |

186 | |

187 | /** |

188 | * @brief Computes a difference between timestamps |

189 | * |

190 | * This function computes a difference between timestamps |

191 | * such that it respects wrapping. |

192 | * |

193 | * If x wraps around, but y doesn't, the result of x-y is |

194 | * negative and very large. However the real difference is |

195 | * not large. It can be calculated by unwrapping x and then |

196 | * calculating x-y. |

197 | * |

198 | * @param x First timestamp |

199 | * @param y Second timestamp |

200 | * @return the difference x-y, unwrapped |

201 | */ |

202 | static inline int64_t diffTicks(int64_t x, int64_t y) { |

203 | int64_t diff=(int64_t)x - (int64_t)y; |

204 | |

205 | // the maximal difference we allow (64secs) |

206 | const int64_t wrapvalue=((int64_t)TICKS_PER_SECOND)*128LL; |

207 | const int64_t max=wrapvalue/2LL; |

208 | |

209 | if(diff > max) { |

210 | // this means that y has wrapped, but |

211 | // x has not. we should unwrap y |

212 | // by adding TICKS_PER_SECOND*128L, meaning that we should substract |

213 | // this value from diff |

214 | diff -= wrapvalue; |

215 | } else if (diff < -max) { |

216 | // this means that x has wrapped, but |

217 | // y has not. we should unwrap x |

218 | // by adding TICKS_PER_SECOND*128L, meaning that we should add |

219 | // this value to diff |

220 | diff += wrapvalue; |

221 | } |

222 | |

223 | #ifdef DEBUG |

224 | if(diff > max || diff < -max) { |

225 | debugWarning("difference does not make any sense\n"); |

226 | debugWarning("diff=%lld max=%lld\n", diff, max); |

227 | |

228 | } |

229 | #endif |

230 | |

231 | return (int64_t)diff; |

232 | |

233 | } |

234 | |

235 | /** |

236 | * @brief Computes a sum of timestamps |

237 | * |

238 | * This function computes a sum of timestamps in ticks, |

239 | * wrapping the result if necessary. |

240 | * |

241 | * @param x First timestamp |

242 | * @param y Second timestamp |

243 | * @return the sum x+y, wrapped |

244 | */ |

245 | static inline uint64_t addTicks(uint64_t x, uint64_t y) { |

246 | uint64_t sum=x+y; |

247 | |

248 | return wrapAtMaxTicks(sum); |

249 | } |

250 | |

251 | /** |

252 | * @brief Computes a substraction of timestamps |

253 | * |

254 | * This function computes a substraction of timestamps in ticks, |

255 | * wrapping the result if necessary. |

256 | * |

257 | * @param x First timestamp |

258 | * @param y Second timestamp |

259 | * @return the difference x-y, wrapped |

260 | */ |

261 | static inline uint64_t substractTicks(uint64_t x, uint64_t y) { |

262 | int64_t subs=x-y; |

263 | |

264 | return wrapAtMinTicks(subs); |

265 | } |

266 | |

267 | /** |

268 | * @brief Converts a received SYT timestamp to a full timestamp in ticks. |

269 | * |

270 | * |

271 | * @param syt_timestamp The SYT timestamp as present in the packet |

272 | * @param rcv_cycle The cycle this timestamp was received on |

273 | * @param ctr_now The current value of the cycle timer ('now') |

274 | * @return |

275 | */ |

276 | static inline uint64_t sytRecvToFullTicks(uint64_t syt_timestamp, unsigned int rcv_cycle, uint64_t ctr_now) { |

277 | uint64_t timestamp; |

278 | |

279 | debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"SYT=%04llX CY=%u CTR=%08llX\n", |

280 | syt_timestamp, rcv_cycle, ctr_now); |

281 | |

282 | // reconstruct the full cycle |

283 | uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now); |

284 | uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now); |

285 | |

286 | // the cycletimer has wrapped since this packet was received |

287 | // we want cc_seconds to reflect the 'seconds' at the point this |

288 | // was received |

289 | if (rcv_cycle>cc_cycles) { |

290 | if (cc_seconds) { |

291 | cc_seconds--; |

292 | } else { |

293 | // seconds has wrapped around, so we'd better not substract 1 |

294 | // the good value is 127 |

295 | cc_seconds=127; |

296 | } |

297 | } |

298 | |

299 | // reconstruct the top part of the timestamp using the current cycle number |

300 | uint64_t rcv_cycle_masked=rcv_cycle & 0xF; |

301 | uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp); |

302 | |

303 | // if this is true, wraparound has occurred, undo this wraparound |

304 | if(syt_cycle<rcv_cycle_masked) syt_cycle += 0x10; |

305 | |

306 | // this is the difference in cycles wrt the cycle the |

307 | // timestamp was received |

308 | uint64_t delta_cycles=syt_cycle-rcv_cycle_masked; |

309 | |

310 | // reconstruct the cycle part of the timestamp |

311 | uint64_t new_cycles=rcv_cycle + delta_cycles; |

312 | |

313 | // if the cycles cause a wraparound of the cycle timer, |

314 | // perform this wraparound |

315 | // and convert the timestamp into ticks |

316 | if(new_cycles<8000) { |

317 | timestamp = new_cycles * TICKS_PER_CYCLE; |

318 | } else { |

319 | debugOutput(DEBUG_LEVEL_VERY_VERBOSE, |

320 | "Detected wraparound: %d + %d = %d\n", |

321 | rcv_cycle,delta_cycles,new_cycles); |

322 | |

323 | new_cycles-=8000; // wrap around |

324 | #ifdef DEBUG |

325 | if (new_cycles >= 8000) { |

326 | debugWarning("insufficient unwrapping\n"); |

327 | } |

328 | #endif |

329 | timestamp = new_cycles * TICKS_PER_CYCLE; |

330 | // add one second due to wraparound |

331 | timestamp += TICKS_PER_SECOND; |

332 | } |

333 | |

334 | timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp); |

335 | |

336 | timestamp = addTicks(timestamp, cc_seconds * TICKS_PER_SECOND); |

337 | |

338 | #ifdef DEBUG |

339 | if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) { |

340 | debugWarning("back-converted timestamp not equal to SYT\n"); |

341 | debugWarning("TS=%011llu TSC=%08lX SYT=%04X\n", |

342 | timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp); |

343 | } |

344 | #endif |

345 | |

346 | return timestamp; |

347 | } |

348 | |

349 | /** |

350 | * @brief Converts a transmit SYT timestamp to a full timestamp in ticks. |

351 | * |

352 | * The difference between sytRecvToFullTicks and sytXmitToFullTicks is |

353 | * the way SYT cycle wraparound is detected: in the receive version, |

354 | * wraparound is present if rcv_cycle > current_cycle. In the xmit |

355 | * version this is when current_cycle > xmt_cycle. |

356 | * |

357 | * @param syt_timestamp The SYT timestamp as present in the packet |

358 | * @param xmt_cycle The cycle this timestamp was received on |

359 | * @param ctr_now The current value of the cycle timer ('now') |

360 | * @return |

361 | */ |

362 | static inline uint64_t sytXmitToFullTicks(uint64_t syt_timestamp, unsigned int xmt_cycle, uint64_t ctr_now) { |

363 | uint64_t timestamp; |

364 | |

365 | debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"SYT=%08llX CY=%04X CTR=%08llX\n", |

366 | syt_timestamp,xmt_cycle,ctr_now); |

367 | |

368 | // reconstruct the full cycle |

369 | uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now); |

370 | uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now); |

371 | |

372 | // the cycletimer has wrapped since this packet was received |

373 | // we want cc_seconds to reflect the 'seconds' at the point this |

374 | // is to be transmitted |

375 | if (xmt_cycle<cc_cycles) { |

376 | if (cc_seconds) { |

377 | cc_seconds--; |

378 | } else { |

379 | // seconds has wrapped around, so we'd better not substract 1 |

380 | // the good value is 127 |

381 | cc_seconds=127; |

382 | } |

383 | } |

384 | |

385 | // reconstruct the top part of the timestamp using the current cycle number |

386 | uint64_t xmt_cycle_masked=xmt_cycle & 0xF; |

387 | uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp); |

388 | |

389 | // if this is true, wraparound has occurred, undo this wraparound |

390 | if(syt_cycle<xmt_cycle_masked) syt_cycle += 0x10; |

391 | |

392 | // this is the difference in cycles wrt the cycle the |

393 | // timestamp was received |

394 | uint64_t delta_cycles=syt_cycle-xmt_cycle_masked; |

395 | |

396 | // reconstruct the cycle part of the timestamp |

397 | uint64_t new_cycles=xmt_cycle + delta_cycles; |

398 | |

399 | // if the cycles cause a wraparound of the cycle timer, |

400 | // perform this wraparound |

401 | // and convert the timestamp into ticks |

402 | if(new_cycles<8000) { |

403 | timestamp = new_cycles * TICKS_PER_CYCLE; |

404 | } else { |

405 | debugOutput(DEBUG_LEVEL_VERY_VERBOSE, |

406 | "Detected wraparound: %d + %d = %d\n", |

407 | xmt_cycle,delta_cycles,new_cycles); |

408 | |

409 | new_cycles-=8000; // wrap around |

410 | #ifdef DEBUG |

411 | if (new_cycles >= 8000) { |

412 | debugWarning("insufficient unwrapping\n"); |

413 | } |

414 | #endif |

415 | timestamp = new_cycles * TICKS_PER_CYCLE; |

416 | // add one second due to wraparound |

417 | timestamp += TICKS_PER_SECOND; |

418 | } |

419 | |

420 | timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp); |

421 | |

422 | timestamp = addTicks(timestamp, cc_seconds * TICKS_PER_SECOND); |

423 | |

424 | #ifdef DEBUG |

425 | if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) { |

426 | debugWarning("back-converted timestamp not equal to SYT\n"); |

427 | debugWarning("TS=%011llu TSC=%08lX SYT=%04X\n", |

428 | timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp); |

429 | } |

430 | #endif |

431 | |

432 | return timestamp; |

433 | } |

434 | |

435 | #endif // __CYCLETIMER_H__ |

**Note:**See TracBrowser for help on using the browser.