# root/trunk/libffado/src/libieee1394/cycletimer.h

Revision 879, 14.1 kB (checked in by ppalmers, 13 years ago) |
---|

Line | |
---|---|

1 | /* |

2 | * Copyright (C) 2005-2008 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 2 of the License, or |

12 | * (at your option) version 3 of the License. |

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_HALFCYCLE (3072U/2U) |

39 | #define TICKS_PER_SECOND 24576000UL |

40 | #define TICKS_PER_USEC (24.576000) |

41 | |

42 | #define USECS_PER_TICK (1.0/TICKS_PER_USEC) |

43 | #define USECS_PER_CYCLE (125U) |

44 | |

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

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

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

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

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

50 | (CYCLE_TIMER_GET_OFFSET(x) )) |

51 | |

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

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

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

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

56 | |

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

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

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

60 | |

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

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

63 | |

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

65 | + (127ULL * TICKS_PER_SECOND) \ |

66 | + (CYCLES_PER_SECOND * TICKS_PER_CYCLE) \ |

67 | + (TICKS_PER_CYCLE) \ |

68 | ) |

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

70 | |

71 | #define INVALID_TIMESTAMP_TICKS 0xFFFFFFFFFFFFFFFFULL |

72 | |

73 | DECLARE_GLOBAL_DEBUG_MODULE; |

74 | |

75 | /** |

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

77 | * |

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

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

80 | * |

81 | * @param x time to wrap |

82 | * @return wrapped time |

83 | */ |

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

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

86 | x -= TICKS_PER_SECOND * 128L; |

87 | } |

88 | |

89 | #ifdef DEBUG |

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

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

92 | } |

93 | #endif |

94 | |

95 | return x; |

96 | } |

97 | |

98 | /** |

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

100 | * |

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

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

103 | * |

104 | * @param x time to wrap |

105 | * @return wrapped time |

106 | */ |

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

108 | if (x < 0) { |

109 | x += TICKS_PER_SECOND * 128L; |

110 | } |

111 | |

112 | #ifdef DEBUG |

113 | if (x < 0) { |

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

115 | |

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

117 | while (x < 0) { |

118 | x += TICKS_PER_SECOND * 128L; |

119 | |

120 | if (x < 0) { |

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

122 | } |

123 | } |

124 | } |

125 | |

126 | #endif |

127 | |

128 | return (int64_t)x; |

129 | } |

130 | |

131 | /** |

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

133 | * |

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

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

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

137 | * |

138 | * @param x value to wrap |

139 | * @return wrapped value |

140 | */ |

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

142 | |

143 | if (x < 0) { |

144 | x += TICKS_PER_SECOND * 128L; |

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

146 | x -= TICKS_PER_SECOND * 128L; |

147 | } |

148 | |

149 | #ifdef DEBUG |

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

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

152 | } |

153 | if (x < 0) { |

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

155 | } |

156 | #endif |

157 | return x; |

158 | |

159 | } |

160 | |

161 | /** |

162 | * @brief Computes the sum of two cycle values |

163 | * |

164 | * This function computes a sum between cycles |

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

166 | * |

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

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

169 | * |

170 | * See addTicks |

171 | * |

172 | * @param x First cycle value |

173 | * @param y Second cycle value |

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

175 | */ |

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

177 | unsigned int sum = x + y; |

178 | #ifdef DEBUG |

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

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

181 | } |

182 | #endif |

183 | |

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

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

186 | return sum; |

187 | } |

188 | |

189 | /** |

190 | * @brief Computes a difference between cycles |

191 | * |

192 | * This function computes a difference between cycles |

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

194 | * |

195 | * See diffTicks |

196 | * |

197 | * @param x First cycle value |

198 | * @param y Second cycle value |

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

200 | */ |

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

202 | int diff = x - y; |

203 | |

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

205 | const int max=CYCLES_PER_SECOND/2; |

206 | |

207 | if(diff > max) { |

208 | diff -= CYCLES_PER_SECOND; |

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

210 | diff += CYCLES_PER_SECOND; |

211 | } |

212 | |

213 | return diff; |

214 | } |

215 | |

216 | /** |

217 | * @brief Computes a difference between timestamps |

218 | * |

219 | * This function computes a difference between timestamps |

220 | * such that it respects wrapping. |

221 | * |

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

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

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

225 | * calculating x-y. |

226 | * |

227 | * @param x First timestamp |

228 | * @param y Second timestamp |

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

230 | */ |

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

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

233 | |

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

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

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

237 | |

238 | if(diff > max) { |

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

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

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

242 | // this value from diff |

243 | diff -= wrapvalue; |

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

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

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

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

248 | // this value to diff |

249 | diff += wrapvalue; |

250 | } |

251 | |

252 | #ifdef DEBUG |

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

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

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

256 | |

257 | } |

258 | #endif |

259 | |

260 | return (int64_t)diff; |

261 | |

262 | } |

263 | |

264 | /** |

265 | * @brief Computes a sum of timestamps |

266 | * |

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

268 | * wrapping the result if necessary. |

269 | * |

270 | * @param x First timestamp |

271 | * @param y Second timestamp |

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

273 | */ |

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

275 | uint64_t sum=x+y; |

276 | |

277 | return wrapAtMaxTicks(sum); |

278 | } |

279 | |

280 | /** |

281 | * @brief Computes a substraction of timestamps |

282 | * |

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

284 | * wrapping the result if necessary. |

285 | * |

286 | * @param x First timestamp |

287 | * @param y Second timestamp |

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

289 | */ |

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

291 | int64_t subs=x-y; |

292 | |

293 | return wrapAtMinTicks(subs); |

294 | } |

295 | |

296 | /** |

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

298 | * |

299 | * |

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

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

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

303 | * @return |

304 | */ |

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

306 | uint64_t timestamp; |

307 | |

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

309 | syt_timestamp, rcv_cycle, ctr_now); |

310 | |

311 | // reconstruct the full cycle |

312 | uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now); |

313 | uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now); |

314 | |

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

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

317 | // was received |

318 | if (rcv_cycle>cc_cycles) { |

319 | if (cc_seconds) { |

320 | cc_seconds--; |

321 | } else { |

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

323 | // the good value is 127 |

324 | cc_seconds=127; |

325 | } |

326 | } |

327 | |

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

329 | uint64_t rcv_cycle_masked=rcv_cycle & 0xF; |

330 | uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp); |

331 | |

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

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

334 | |

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

336 | // timestamp was received |

337 | uint64_t delta_cycles=syt_cycle-rcv_cycle_masked; |

338 | |

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

340 | uint64_t new_cycles=rcv_cycle + delta_cycles; |

341 | |

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

343 | // perform this wraparound |

344 | // and convert the timestamp into ticks |

345 | if(new_cycles<8000) { |

346 | timestamp = new_cycles * TICKS_PER_CYCLE; |

347 | } else { |

348 | debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, |

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

350 | rcv_cycle, delta_cycles, new_cycles); |

351 | |

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

353 | #ifdef DEBUG |

354 | if (new_cycles >= 8000) { |

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

356 | } |

357 | #endif |

358 | timestamp = new_cycles * TICKS_PER_CYCLE; |

359 | // add one second due to wraparound |

360 | timestamp += TICKS_PER_SECOND; |

361 | } |

362 | |

363 | timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp); |

364 | |

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

366 | |

367 | #ifdef DEBUG |

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

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

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

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

372 | } |

373 | #endif |

374 | |

375 | return timestamp; |

376 | } |

377 | |

378 | /** |

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

380 | * |

381 | * The difference between sytRecvToFullTicks and sytXmitToFullTicks is |

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

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

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

385 | * |

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

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

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

389 | * @return |

390 | */ |

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

392 | uint64_t timestamp; |

393 | |

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

395 | syt_timestamp, xmt_cycle, ctr_now); |

396 | |

397 | // reconstruct the full cycle |

398 | uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now); |

399 | uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now); |

400 | |

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

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

403 | // is to be transmitted |

404 | if (xmt_cycle<cc_cycles) { |

405 | if (cc_seconds) { |

406 | cc_seconds--; |

407 | } else { |

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

409 | // the good value is 127 |

410 | cc_seconds=127; |

411 | } |

412 | } |

413 | |

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

415 | uint64_t xmt_cycle_masked=xmt_cycle & 0xF; |

416 | uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp); |

417 | |

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

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

420 | |

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

422 | // timestamp was received |

423 | uint64_t delta_cycles=syt_cycle-xmt_cycle_masked; |

424 | |

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

426 | uint64_t new_cycles=xmt_cycle + delta_cycles; |

427 | |

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

429 | // perform this wraparound |

430 | // and convert the timestamp into ticks |

431 | if(new_cycles<8000) { |

432 | timestamp = new_cycles * TICKS_PER_CYCLE; |

433 | } else { |

434 | debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, |

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

436 | xmt_cycle, delta_cycles, new_cycles); |

437 | |

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

439 | #ifdef DEBUG |

440 | if (new_cycles >= 8000) { |

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

442 | } |

443 | #endif |

444 | timestamp = new_cycles * TICKS_PER_CYCLE; |

445 | // add one second due to wraparound |

446 | timestamp += TICKS_PER_SECOND; |

447 | } |

448 | |

449 | timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp); |

450 | |

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

452 | |

453 | #ifdef DEBUG |

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

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

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

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

458 | } |

459 | #endif |

460 | |

461 | return timestamp; |

462 | } |

463 | |

464 | #endif // __CYCLETIMER_H__ |

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