sim.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. /*
  2. * Copyright 2010 Tilera Corporation. All Rights Reserved.
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License
  6. * as published by the Free Software Foundation, version 2.
  7. *
  8. * This program is distributed in the hope that it will be useful, but
  9. * WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  11. * NON INFRINGEMENT. See the GNU General Public License for
  12. * more details.
  13. */
  14. /**
  15. * @file
  16. *
  17. * Provides an API for controlling the simulator at runtime.
  18. */
  19. /**
  20. * @addtogroup arch_sim
  21. * @{
  22. *
  23. * An API for controlling the simulator at runtime.
  24. *
  25. * The simulator's behavior can be modified while it is running.
  26. * For example, human-readable trace output can be enabled and disabled
  27. * around code of interest.
  28. *
  29. * There are two ways to modify simulator behavior:
  30. * programmatically, by calling various sim_* functions, and
  31. * interactively, by entering commands like "sim set functional true"
  32. * at the tile-monitor prompt. Typing "sim help" at that prompt provides
  33. * a list of interactive commands.
  34. *
  35. * All interactive commands can also be executed programmatically by
  36. * passing a string to the sim_command function.
  37. */
  38. #ifndef __ARCH_SIM_H__
  39. #define __ARCH_SIM_H__
  40. #include <arch/sim_def.h>
  41. #include <arch/abi.h>
  42. #ifndef __ASSEMBLER__
  43. #include <arch/spr_def.h>
  44. /**
  45. * Return true if the current program is running under a simulator,
  46. * rather than on real hardware. If running on hardware, other "sim_xxx()"
  47. * calls have no useful effect.
  48. */
  49. static inline int
  50. sim_is_simulator(void)
  51. {
  52. return __insn_mfspr(SPR_SIM_CONTROL) != 0;
  53. }
  54. /**
  55. * Checkpoint the simulator state to a checkpoint file.
  56. *
  57. * The checkpoint file name is either the default or the name specified
  58. * on the command line with "--checkpoint-file".
  59. */
  60. static __inline void
  61. sim_checkpoint(void)
  62. {
  63. __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_CHECKPOINT);
  64. }
  65. /**
  66. * Report whether or not various kinds of simulator tracing are enabled.
  67. *
  68. * @return The bitwise OR of these values:
  69. *
  70. * SIM_TRACE_CYCLES (--trace-cycles),
  71. * SIM_TRACE_ROUTER (--trace-router),
  72. * SIM_TRACE_REGISTER_WRITES (--trace-register-writes),
  73. * SIM_TRACE_DISASM (--trace-disasm),
  74. * SIM_TRACE_STALL_INFO (--trace-stall-info)
  75. * SIM_TRACE_MEMORY_CONTROLLER (--trace-memory-controller)
  76. * SIM_TRACE_L2_CACHE (--trace-l2)
  77. * SIM_TRACE_LINES (--trace-lines)
  78. */
  79. static __inline unsigned int
  80. sim_get_tracing(void)
  81. {
  82. return __insn_mfspr(SPR_SIM_CONTROL) & SIM_TRACE_FLAG_MASK;
  83. }
  84. /**
  85. * Turn on or off different kinds of simulator tracing.
  86. *
  87. * @param mask Either one of these special values:
  88. *
  89. * SIM_TRACE_NONE (turns off tracing),
  90. * SIM_TRACE_ALL (turns on all possible tracing).
  91. *
  92. * or the bitwise OR of these values:
  93. *
  94. * SIM_TRACE_CYCLES (--trace-cycles),
  95. * SIM_TRACE_ROUTER (--trace-router),
  96. * SIM_TRACE_REGISTER_WRITES (--trace-register-writes),
  97. * SIM_TRACE_DISASM (--trace-disasm),
  98. * SIM_TRACE_STALL_INFO (--trace-stall-info)
  99. * SIM_TRACE_MEMORY_CONTROLLER (--trace-memory-controller)
  100. * SIM_TRACE_L2_CACHE (--trace-l2)
  101. * SIM_TRACE_LINES (--trace-lines)
  102. */
  103. static __inline void
  104. sim_set_tracing(unsigned int mask)
  105. {
  106. __insn_mtspr(SPR_SIM_CONTROL, SIM_TRACE_SPR_ARG(mask));
  107. }
  108. /**
  109. * Request dumping of different kinds of simulator state.
  110. *
  111. * @param mask Either this special value:
  112. *
  113. * SIM_DUMP_ALL (dump all known state)
  114. *
  115. * or the bitwise OR of these values:
  116. *
  117. * SIM_DUMP_REGS (the register file),
  118. * SIM_DUMP_SPRS (the SPRs),
  119. * SIM_DUMP_ITLB (the iTLB),
  120. * SIM_DUMP_DTLB (the dTLB),
  121. * SIM_DUMP_L1I (the L1 I-cache),
  122. * SIM_DUMP_L1D (the L1 D-cache),
  123. * SIM_DUMP_L2 (the L2 cache),
  124. * SIM_DUMP_SNREGS (the switch register file),
  125. * SIM_DUMP_SNITLB (the switch iTLB),
  126. * SIM_DUMP_SNL1I (the switch L1 I-cache),
  127. * SIM_DUMP_BACKTRACE (the current backtrace)
  128. */
  129. static __inline void
  130. sim_dump(unsigned int mask)
  131. {
  132. __insn_mtspr(SPR_SIM_CONTROL, SIM_DUMP_SPR_ARG(mask));
  133. }
  134. /**
  135. * Print a string to the simulator stdout.
  136. *
  137. * @param str The string to be written.
  138. */
  139. static __inline void
  140. sim_print(const char* str)
  141. {
  142. for ( ; *str != '\0'; str++)
  143. {
  144. __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC |
  145. (*str << _SIM_CONTROL_OPERATOR_BITS));
  146. }
  147. __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC |
  148. (SIM_PUTC_FLUSH_BINARY << _SIM_CONTROL_OPERATOR_BITS));
  149. }
  150. /**
  151. * Print a string to the simulator stdout.
  152. *
  153. * @param str The string to be written (a newline is automatically added).
  154. */
  155. static __inline void
  156. sim_print_string(const char* str)
  157. {
  158. for ( ; *str != '\0'; str++)
  159. {
  160. __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC |
  161. (*str << _SIM_CONTROL_OPERATOR_BITS));
  162. }
  163. __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC |
  164. (SIM_PUTC_FLUSH_STRING << _SIM_CONTROL_OPERATOR_BITS));
  165. }
  166. /**
  167. * Execute a simulator command string.
  168. *
  169. * Type 'sim help' at the tile-monitor prompt to learn what commands
  170. * are available. Note the use of the tile-monitor "sim" command to
  171. * pass commands to the simulator.
  172. *
  173. * The argument to sim_command() does not include the leading "sim"
  174. * prefix used at the tile-monitor prompt; for example, you might call
  175. * sim_command("trace disasm").
  176. */
  177. static __inline void
  178. sim_command(const char* str)
  179. {
  180. int c;
  181. do
  182. {
  183. c = *str++;
  184. __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_COMMAND |
  185. (c << _SIM_CONTROL_OPERATOR_BITS));
  186. }
  187. while (c);
  188. }
  189. #ifndef __DOXYGEN__
  190. /**
  191. * The underlying implementation of "_sim_syscall()".
  192. *
  193. * We use extra "and" instructions to ensure that all the values
  194. * we are passing to the simulator are actually valid in the registers
  195. * (i.e. returned from memory) prior to the SIM_CONTROL spr.
  196. */
  197. static __inline long _sim_syscall0(int val)
  198. {
  199. long result;
  200. __asm__ __volatile__ ("mtspr SIM_CONTROL, r0"
  201. : "=R00" (result) : "R00" (val));
  202. return result;
  203. }
  204. static __inline long _sim_syscall1(int val, long arg1)
  205. {
  206. long result;
  207. __asm__ __volatile__ ("{ and zero, r1, r1; mtspr SIM_CONTROL, r0 }"
  208. : "=R00" (result) : "R00" (val), "R01" (arg1));
  209. return result;
  210. }
  211. static __inline long _sim_syscall2(int val, long arg1, long arg2)
  212. {
  213. long result;
  214. __asm__ __volatile__ ("{ and zero, r1, r2; mtspr SIM_CONTROL, r0 }"
  215. : "=R00" (result)
  216. : "R00" (val), "R01" (arg1), "R02" (arg2));
  217. return result;
  218. }
  219. /* Note that _sim_syscall3() and higher are technically at risk of
  220. receiving an interrupt right before the mtspr bundle, in which case
  221. the register values for arguments 3 and up may still be in flight
  222. to the core from a stack frame reload. */
  223. static __inline long _sim_syscall3(int val, long arg1, long arg2, long arg3)
  224. {
  225. long result;
  226. __asm__ __volatile__ ("{ and zero, r3, r3 };"
  227. "{ and zero, r1, r2; mtspr SIM_CONTROL, r0 }"
  228. : "=R00" (result)
  229. : "R00" (val), "R01" (arg1), "R02" (arg2),
  230. "R03" (arg3));
  231. return result;
  232. }
  233. static __inline long _sim_syscall4(int val, long arg1, long arg2, long arg3,
  234. long arg4)
  235. {
  236. long result;
  237. __asm__ __volatile__ ("{ and zero, r3, r4 };"
  238. "{ and zero, r1, r2; mtspr SIM_CONTROL, r0 }"
  239. : "=R00" (result)
  240. : "R00" (val), "R01" (arg1), "R02" (arg2),
  241. "R03" (arg3), "R04" (arg4));
  242. return result;
  243. }
  244. static __inline long _sim_syscall5(int val, long arg1, long arg2, long arg3,
  245. long arg4, long arg5)
  246. {
  247. long result;
  248. __asm__ __volatile__ ("{ and zero, r3, r4; and zero, r5, r5 };"
  249. "{ and zero, r1, r2; mtspr SIM_CONTROL, r0 }"
  250. : "=R00" (result)
  251. : "R00" (val), "R01" (arg1), "R02" (arg2),
  252. "R03" (arg3), "R04" (arg4), "R05" (arg5));
  253. return result;
  254. }
  255. /**
  256. * Make a special syscall to the simulator itself, if running under
  257. * simulation. This is used as the implementation of other functions
  258. * and should not be used outside this file.
  259. *
  260. * @param syscall_num The simulator syscall number.
  261. * @param nr The number of additional arguments provided.
  262. *
  263. * @return Varies by syscall.
  264. */
  265. #define _sim_syscall(syscall_num, nr, args...) \
  266. _sim_syscall##nr( \
  267. ((syscall_num) << _SIM_CONTROL_OPERATOR_BITS) | SIM_CONTROL_SYSCALL, \
  268. ##args)
  269. /* Values for the "access_mask" parameters below. */
  270. #define SIM_WATCHPOINT_READ 1
  271. #define SIM_WATCHPOINT_WRITE 2
  272. #define SIM_WATCHPOINT_EXECUTE 4
  273. static __inline int
  274. sim_add_watchpoint(unsigned int process_id,
  275. unsigned long address,
  276. unsigned long size,
  277. unsigned int access_mask,
  278. unsigned long user_data)
  279. {
  280. return _sim_syscall(SIM_SYSCALL_ADD_WATCHPOINT, 5, process_id,
  281. address, size, access_mask, user_data);
  282. }
  283. static __inline int
  284. sim_remove_watchpoint(unsigned int process_id,
  285. unsigned long address,
  286. unsigned long size,
  287. unsigned int access_mask,
  288. unsigned long user_data)
  289. {
  290. return _sim_syscall(SIM_SYSCALL_REMOVE_WATCHPOINT, 5, process_id,
  291. address, size, access_mask, user_data);
  292. }
  293. /**
  294. * Return value from sim_query_watchpoint.
  295. */
  296. struct SimQueryWatchpointStatus
  297. {
  298. /**
  299. * 0 if a watchpoint fired, 1 if no watchpoint fired, or -1 for
  300. * error (meaning a bad process_id).
  301. */
  302. int syscall_status;
  303. /**
  304. * The address of the watchpoint that fired (this is the address
  305. * passed to sim_add_watchpoint, not an address within that range
  306. * that actually triggered the watchpoint).
  307. */
  308. unsigned long address;
  309. /** The arbitrary user_data installed by sim_add_watchpoint. */
  310. unsigned long user_data;
  311. };
  312. static __inline struct SimQueryWatchpointStatus
  313. sim_query_watchpoint(unsigned int process_id)
  314. {
  315. struct SimQueryWatchpointStatus status;
  316. long val = SIM_CONTROL_SYSCALL |
  317. (SIM_SYSCALL_QUERY_WATCHPOINT << _SIM_CONTROL_OPERATOR_BITS);
  318. __asm__ __volatile__ ("{ and zero, r1, r1; mtspr SIM_CONTROL, r0 }"
  319. : "=R00" (status.syscall_status),
  320. "=R01" (status.address),
  321. "=R02" (status.user_data)
  322. : "R00" (val), "R01" (process_id));
  323. return status;
  324. }
  325. /* On the simulator, confirm lines have been evicted everywhere. */
  326. static __inline void
  327. sim_validate_lines_evicted(unsigned long long pa, unsigned long length)
  328. {
  329. #ifdef __LP64__
  330. _sim_syscall(SIM_SYSCALL_VALIDATE_LINES_EVICTED, 2, pa, length);
  331. #else
  332. _sim_syscall(SIM_SYSCALL_VALIDATE_LINES_EVICTED, 4,
  333. 0 /* dummy */, (long)(pa), (long)(pa >> 32), length);
  334. #endif
  335. }
  336. /* Return the current CPU speed in cycles per second. */
  337. static __inline long
  338. sim_query_cpu_speed(void)
  339. {
  340. return _sim_syscall(SIM_SYSCALL_QUERY_CPU_SPEED, 0);
  341. }
  342. #endif /* !__DOXYGEN__ */
  343. /**
  344. * Modify the shaping parameters of a shim.
  345. *
  346. * @param shim The shim to modify. One of:
  347. * SIM_CONTROL_SHAPING_GBE_0
  348. * SIM_CONTROL_SHAPING_GBE_1
  349. * SIM_CONTROL_SHAPING_GBE_2
  350. * SIM_CONTROL_SHAPING_GBE_3
  351. * SIM_CONTROL_SHAPING_XGBE_0
  352. * SIM_CONTROL_SHAPING_XGBE_1
  353. *
  354. * @param type The type of shaping. This should be the same type of
  355. * shaping that is already in place on the shim. One of:
  356. * SIM_CONTROL_SHAPING_MULTIPLIER
  357. * SIM_CONTROL_SHAPING_PPS
  358. * SIM_CONTROL_SHAPING_BPS
  359. *
  360. * @param units The magnitude of the rate. One of:
  361. * SIM_CONTROL_SHAPING_UNITS_SINGLE
  362. * SIM_CONTROL_SHAPING_UNITS_KILO
  363. * SIM_CONTROL_SHAPING_UNITS_MEGA
  364. * SIM_CONTROL_SHAPING_UNITS_GIGA
  365. *
  366. * @param rate The rate to which to change it. This must fit in
  367. * SIM_CONTROL_SHAPING_RATE_BITS bits or a warning is issued and
  368. * the shaping is not changed.
  369. *
  370. * @return 0 if no problems were detected in the arguments to sim_set_shaping
  371. * or 1 if problems were detected (for example, rate does not fit in 17 bits).
  372. */
  373. static __inline int
  374. sim_set_shaping(unsigned shim,
  375. unsigned type,
  376. unsigned units,
  377. unsigned rate)
  378. {
  379. if ((rate & ~((1 << SIM_CONTROL_SHAPING_RATE_BITS) - 1)) != 0)
  380. return 1;
  381. __insn_mtspr(SPR_SIM_CONTROL, SIM_SHAPING_SPR_ARG(shim, type, units, rate));
  382. return 0;
  383. }
  384. #ifdef __tilegx__
  385. /** Enable a set of mPIPE links. Pass a -1 link_mask to enable all links. */
  386. static __inline void
  387. sim_enable_mpipe_links(unsigned mpipe, unsigned long link_mask)
  388. {
  389. __insn_mtspr(SPR_SIM_CONTROL,
  390. (SIM_CONTROL_ENABLE_MPIPE_LINK_MAGIC_BYTE |
  391. (mpipe << 8) | (1 << 16) | ((uint_reg_t)link_mask << 32)));
  392. }
  393. /** Disable a set of mPIPE links. Pass a -1 link_mask to disable all links. */
  394. static __inline void
  395. sim_disable_mpipe_links(unsigned mpipe, unsigned long link_mask)
  396. {
  397. __insn_mtspr(SPR_SIM_CONTROL,
  398. (SIM_CONTROL_ENABLE_MPIPE_LINK_MAGIC_BYTE |
  399. (mpipe << 8) | (0 << 16) | ((uint_reg_t)link_mask << 32)));
  400. }
  401. #endif /* __tilegx__ */
  402. /*
  403. * An API for changing "functional" mode.
  404. */
  405. #ifndef __DOXYGEN__
  406. #define sim_enable_functional() \
  407. __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_ENABLE_FUNCTIONAL)
  408. #define sim_disable_functional() \
  409. __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_DISABLE_FUNCTIONAL)
  410. #endif /* __DOXYGEN__ */
  411. /*
  412. * Profiler support.
  413. */
  414. /**
  415. * Turn profiling on for the current task.
  416. *
  417. * Note that this has no effect if run in an environment without
  418. * profiling support (thus, the proper flags to the simulator must
  419. * be supplied).
  420. */
  421. static __inline void
  422. sim_profiler_enable(void)
  423. {
  424. __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PROFILER_ENABLE);
  425. }
  426. /** Turn profiling off for the current task. */
  427. static __inline void
  428. sim_profiler_disable(void)
  429. {
  430. __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PROFILER_DISABLE);
  431. }
  432. /**
  433. * Turn profiling on or off for the current task.
  434. *
  435. * @param enabled If true, turns on profiling. If false, turns it off.
  436. *
  437. * Note that this has no effect if run in an environment without
  438. * profiling support (thus, the proper flags to the simulator must
  439. * be supplied).
  440. */
  441. static __inline void
  442. sim_profiler_set_enabled(int enabled)
  443. {
  444. int val =
  445. enabled ? SIM_CONTROL_PROFILER_ENABLE : SIM_CONTROL_PROFILER_DISABLE;
  446. __insn_mtspr(SPR_SIM_CONTROL, val);
  447. }
  448. /**
  449. * Return true if and only if profiling is currently enabled
  450. * for the current task.
  451. *
  452. * This returns false even if sim_profiler_enable() was called
  453. * if the current execution environment does not support profiling.
  454. */
  455. static __inline int
  456. sim_profiler_is_enabled(void)
  457. {
  458. return ((__insn_mfspr(SPR_SIM_CONTROL) & SIM_PROFILER_ENABLED_MASK) != 0);
  459. }
  460. /**
  461. * Reset profiling counters to zero for the current task.
  462. *
  463. * Resetting can be done while profiling is enabled. It does not affect
  464. * the chip-wide profiling counters.
  465. */
  466. static __inline void
  467. sim_profiler_clear(void)
  468. {
  469. __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PROFILER_CLEAR);
  470. }
  471. /**
  472. * Enable specified chip-level profiling counters.
  473. *
  474. * Does not affect the per-task profiling counters.
  475. *
  476. * @param mask Either this special value:
  477. *
  478. * SIM_CHIP_ALL (enables all chip-level components).
  479. *
  480. * or the bitwise OR of these values:
  481. *
  482. * SIM_CHIP_MEMCTL (enable all memory controllers)
  483. * SIM_CHIP_XAUI (enable all XAUI controllers)
  484. * SIM_CHIP_MPIPE (enable all MPIPE controllers)
  485. */
  486. static __inline void
  487. sim_profiler_chip_enable(unsigned int mask)
  488. {
  489. __insn_mtspr(SPR_SIM_CONTROL, SIM_PROFILER_CHIP_ENABLE_SPR_ARG(mask));
  490. }
  491. /**
  492. * Disable specified chip-level profiling counters.
  493. *
  494. * Does not affect the per-task profiling counters.
  495. *
  496. * @param mask Either this special value:
  497. *
  498. * SIM_CHIP_ALL (disables all chip-level components).
  499. *
  500. * or the bitwise OR of these values:
  501. *
  502. * SIM_CHIP_MEMCTL (disable all memory controllers)
  503. * SIM_CHIP_XAUI (disable all XAUI controllers)
  504. * SIM_CHIP_MPIPE (disable all MPIPE controllers)
  505. */
  506. static __inline void
  507. sim_profiler_chip_disable(unsigned int mask)
  508. {
  509. __insn_mtspr(SPR_SIM_CONTROL, SIM_PROFILER_CHIP_DISABLE_SPR_ARG(mask));
  510. }
  511. /**
  512. * Reset specified chip-level profiling counters to zero.
  513. *
  514. * Does not affect the per-task profiling counters.
  515. *
  516. * @param mask Either this special value:
  517. *
  518. * SIM_CHIP_ALL (clears all chip-level components).
  519. *
  520. * or the bitwise OR of these values:
  521. *
  522. * SIM_CHIP_MEMCTL (clear all memory controllers)
  523. * SIM_CHIP_XAUI (clear all XAUI controllers)
  524. * SIM_CHIP_MPIPE (clear all MPIPE controllers)
  525. */
  526. static __inline void
  527. sim_profiler_chip_clear(unsigned int mask)
  528. {
  529. __insn_mtspr(SPR_SIM_CONTROL, SIM_PROFILER_CHIP_CLEAR_SPR_ARG(mask));
  530. }
  531. /*
  532. * Event support.
  533. */
  534. #ifndef __DOXYGEN__
  535. static __inline void
  536. sim_event_begin(unsigned int x)
  537. {
  538. #if defined(__tile__) && !defined(__NO_EVENT_SPR__)
  539. __insn_mtspr(SPR_EVENT_BEGIN, x);
  540. #endif
  541. }
  542. static __inline void
  543. sim_event_end(unsigned int x)
  544. {
  545. #if defined(__tile__) && !defined(__NO_EVENT_SPR__)
  546. __insn_mtspr(SPR_EVENT_END, x);
  547. #endif
  548. }
  549. #endif /* !__DOXYGEN__ */
  550. #endif /* !__ASSEMBLER__ */
  551. #endif /* !__ARCH_SIM_H__ */
  552. /** @} */