# root/trunk/libffado/src/libstreaming/util/cycletimer.h

Revision 742, 13.9 kB (checked in by ppalmers, 14 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 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 3 of the License, or |

12 | * (at your option) any later version. |

13 | * |

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. |

18 | * |

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/>. |

21 | * |

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 the sum of two cycle values |

162 | * |

163 | * This function computes a sum between cycles |

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

165 | * |

166 | * The passed arguments are assumed to be valid cycle numbers, |

167 | * i.e. they should be wrapped at 8000 cycles |

168 | * |

169 | * See addTicks |

170 | * |

171 | * @param x First cycle value |

172 | * @param y Second cycle value |

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

174 | */ |

175 | static inline unsigned int addCycles(unsigned int x, unsigned int y) { |

176 | unsigned int sum = x + y; |

177 | #ifdef DEBUG |

178 | if (x >= CYCLES_PER_SECOND || y >= CYCLES_PER_SECOND ) { |

179 | debugWarning("At least one argument not wrapped correctly: x=%u, y=%u\n",x,y); |

180 | } |

181 | #endif |

182 | |

183 | // since both x and y are < CYCLES_PER_SECOND this should be enough to unwrap |

184 | if (sum > CYCLES_PER_SECOND) sum -= CYCLES_PER_SECOND; |

185 | return sum; |

186 | } |

187 | |

188 | /** |

189 | * @brief Computes a difference between cycles |

190 | * |

191 | * This function computes a difference between cycles |

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

193 | * |

194 | * See diffTicks |

195 | * |

196 | * @param x First cycle value |

197 | * @param y Second cycle value |

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

199 | */ |

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

201 | int diff = x - y; |

202 | |

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

204 | const int max=CYCLES_PER_SECOND/2; |

205 | |

206 | if(diff > max) { |

207 | diff -= CYCLES_PER_SECOND; |

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

209 | diff += CYCLES_PER_SECOND; |

210 | } |

211 | |

212 | return diff; |

213 | } |

214 | |

215 | /** |

216 | * @brief Computes a difference between timestamps |

217 | * |

218 | * This function computes a difference between timestamps |

219 | * such that it respects wrapping. |

220 | * |

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

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

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

224 | * calculating x-y. |

225 | * |

226 | * @param x First timestamp |

227 | * @param y Second timestamp |

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

229 | */ |

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

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

232 | |

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

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

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

236 | |

237 | if(diff > max) { |

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

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

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

241 | // this value from diff |

242 | diff -= wrapvalue; |

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

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

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

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

247 | // this value to diff |

248 | diff += wrapvalue; |

249 | } |

250 | |

251 | #ifdef DEBUG |

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

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

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

255 | |

256 | } |

257 | #endif |

258 | |

259 | return (int64_t)diff; |

260 | |

261 | } |

262 | |

263 | /** |

264 | * @brief Computes a sum of timestamps |

265 | * |

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

267 | * wrapping the result if necessary. |

268 | * |

269 | * @param x First timestamp |

270 | * @param y Second timestamp |

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

272 | */ |

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

274 | uint64_t sum=x+y; |

275 | |

276 | return wrapAtMaxTicks(sum); |

277 | } |

278 | |

279 | /** |

280 | * @brief Computes a substraction of timestamps |

281 | * |

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

283 | * wrapping the result if necessary. |

284 | * |

285 | * @param x First timestamp |

286 | * @param y Second timestamp |

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

288 | */ |

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

290 | int64_t subs=x-y; |

291 | |

292 | return wrapAtMinTicks(subs); |

293 | } |

294 | |

295 | /** |

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

297 | * |

298 | * |

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

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

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

302 | * @return |

303 | */ |

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

305 | uint64_t timestamp; |

306 | |

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

308 | syt_timestamp, rcv_cycle, ctr_now); |

309 | |

310 | // reconstruct the full cycle |

311 | uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now); |

312 | uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now); |

313 | |

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

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

316 | // was received |

317 | if (rcv_cycle>cc_cycles) { |

318 | if (cc_seconds) { |

319 | cc_seconds--; |

320 | } else { |

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

322 | // the good value is 127 |

323 | cc_seconds=127; |

324 | } |

325 | } |

326 | |

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

328 | uint64_t rcv_cycle_masked=rcv_cycle & 0xF; |

329 | uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp); |

330 | |

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

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

333 | |

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

335 | // timestamp was received |

336 | uint64_t delta_cycles=syt_cycle-rcv_cycle_masked; |

337 | |

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

339 | uint64_t new_cycles=rcv_cycle + delta_cycles; |

340 | |

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

342 | // perform this wraparound |

343 | // and convert the timestamp into ticks |

344 | if(new_cycles<8000) { |

345 | timestamp = new_cycles * TICKS_PER_CYCLE; |

346 | } else { |

347 | debugOutput(DEBUG_LEVEL_VERY_VERBOSE, |

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

349 | rcv_cycle,delta_cycles,new_cycles); |

350 | |

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

352 | #ifdef DEBUG |

353 | if (new_cycles >= 8000) { |

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

355 | } |

356 | #endif |

357 | timestamp = new_cycles * TICKS_PER_CYCLE; |

358 | // add one second due to wraparound |

359 | timestamp += TICKS_PER_SECOND; |

360 | } |

361 | |

362 | timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp); |

363 | |

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

365 | |

366 | #ifdef DEBUG |

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

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

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

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

371 | } |

372 | #endif |

373 | |

374 | return timestamp; |

375 | } |

376 | |

377 | /** |

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

379 | * |

380 | * The difference between sytRecvToFullTicks and sytXmitToFullTicks is |

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

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

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

384 | * |

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

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

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

388 | * @return |

389 | */ |

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

391 | uint64_t timestamp; |

392 | |

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

394 | syt_timestamp,xmt_cycle,ctr_now); |

395 | |

396 | // reconstruct the full cycle |

397 | uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now); |

398 | uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now); |

399 | |

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

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

402 | // is to be transmitted |

403 | if (xmt_cycle<cc_cycles) { |

404 | if (cc_seconds) { |

405 | cc_seconds--; |

406 | } else { |

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

408 | // the good value is 127 |

409 | cc_seconds=127; |

410 | } |

411 | } |

412 | |

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

414 | uint64_t xmt_cycle_masked=xmt_cycle & 0xF; |

415 | uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp); |

416 | |

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

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

419 | |

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

421 | // timestamp was received |

422 | uint64_t delta_cycles=syt_cycle-xmt_cycle_masked; |

423 | |

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

425 | uint64_t new_cycles=xmt_cycle + delta_cycles; |

426 | |

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

428 | // perform this wraparound |

429 | // and convert the timestamp into ticks |

430 | if(new_cycles<8000) { |

431 | timestamp = new_cycles * TICKS_PER_CYCLE; |

432 | } else { |

433 | debugOutput(DEBUG_LEVEL_VERY_VERBOSE, |

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

435 | xmt_cycle,delta_cycles,new_cycles); |

436 | |

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

438 | #ifdef DEBUG |

439 | if (new_cycles >= 8000) { |

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

441 | } |

442 | #endif |

443 | timestamp = new_cycles * TICKS_PER_CYCLE; |

444 | // add one second due to wraparound |

445 | timestamp += TICKS_PER_SECOND; |

446 | } |

447 | |

448 | timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp); |

449 | |

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

451 | |

452 | #ifdef DEBUG |

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

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

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

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

457 | } |

458 | #endif |

459 | |

460 | return timestamp; |

461 | } |

462 | |

463 | #endif // __CYCLETIMER_H__ |

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