]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/l4util/include/ARCH-amd64/rdtsc.h
update
[l4.git] / l4 / pkg / l4util / include / ARCH-amd64 / rdtsc.h
1 /**
2  * \file
3  * \brief  time stamp counter related functions
4  *
5  * \date   Frank Mehnert <fm3@os.inf.tu-dresden.de>
6  * \ingroup l4util_tsc
7  */
8
9 /*
10  * (c) 2003-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
11  *               Alexander Warg <warg@os.inf.tu-dresden.de>,
12  *               Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
13  *     economic rights: Technische Universität Dresden (Germany)
14  * This file is part of TUD:OS and distributed under the terms of the
15  * GNU Lesser General Public License 2.1.
16  * Please see the COPYING-LGPL-2.1 file for details.
17  */
18
19 #ifndef __l4_rdtsc_h
20 #define __l4_rdtsc_h
21
22 /**
23  * \defgroup l4util_tsc Timestamp Counter
24  * \ingroup l4util_api
25  */
26
27 #include <l4/sys/compiler.h>
28 #include <l4/sys/l4int.h>
29 #include <l4/sys/kip.h>
30
31 EXTERN_C_BEGIN
32
33 /* interface */
34 /**
35  * \addtogroup l4util_tsc
36  */
37 /*@{*/
38
39 #define L4_TSC_INIT_AUTO             0 ///< Automatic init
40 #define L4_TSC_INIT_KERNEL           1 ///< Initialized by kernel
41 #define L4_TSC_INIT_CALIBRATE        2 ///< Initialized by user-level
42
43 extern l4_uint32_t l4_scaler_tsc_to_ns;
44 extern l4_uint32_t l4_scaler_tsc_to_us;
45 extern l4_uint32_t l4_scaler_ns_to_tsc;
46 extern l4_uint32_t l4_scaler_tsc_linux;
47
48 /**
49  * \brief Read current value of CPU-internal time stamp counter.
50  * \return 64-bit time stamp
51  */
52 L4_INLINE l4_cpu_time_t
53 l4_rdtsc (void);
54
55 /**
56  * \brief Read the lest significant 32 bit of the TSC.
57  *
58  * Useful for smaller differences, needs less cycles.
59  */
60 L4_INLINE
61 l4_uint32_t l4_rdtsc_32(void);
62
63 /**
64  * \brief Return current value of CPU-internal performance measurement counter.
65  * \param  nr           Number of counter (0 or 1)
66  * \return 64-bit PMC */
67 L4_INLINE l4_cpu_time_t
68 l4_rdpmc (int nr);
69
70 /**
71  * \brief Return the least significant 32 bit of a performance counter.
72  *
73  * Useful for smaller differences, needs less cycles.
74  */
75 L4_INLINE
76 l4_uint32_t l4_rdpmc_32(int nr);
77
78 /** Convert time stamp to ns value.
79  * \param tsc time value in CPU ticks
80  * \return time value in ns
81  */
82 L4_INLINE l4_uint64_t
83 l4_tsc_to_ns (l4_cpu_time_t tsc);
84
85 /** Convert time stamp into micro seconds value.
86  * \param tsc time value in CPU ticks
87  * \return time value in micro seconds
88  */
89 L4_INLINE l4_uint64_t
90 l4_tsc_to_us (l4_cpu_time_t tsc);
91
92 /** Convert time stamp to s.ns value.
93  * \param tsc time value in CPU ticks
94  * \retval s seconds
95  * \retval ns nano seconds
96  */
97 L4_INLINE void
98 l4_tsc_to_s_and_ns (l4_cpu_time_t tsc, l4_uint32_t *s, l4_uint32_t *ns);
99
100 /**
101  * \brief Convert nano seconds into CPU ticks.
102  * \param ns nano seconds
103  * \return CPU ticks
104  */
105 L4_INLINE l4_cpu_time_t
106 l4_ns_to_tsc (l4_uint64_t ns);
107
108 /**
109  * \brief Wait busy for a small amount of time.
110  * \param ns nano seconds to wait
111  * \attention Not intendet for any use!
112  */
113 L4_INLINE void
114 l4_busy_wait_ns (l4_uint64_t ns);
115
116 /**
117  * \brief Wait busy for a small amount of time.
118  * \param us micro seconds to wait
119  * \attention Not intendet for any use!
120  */
121 L4_INLINE void
122 l4_busy_wait_us (l4_uint64_t us);
123
124 EXTERN_C_BEGIN
125
126 /**
127  * \brief Calibrate scalers for time stamp calculations.
128  *
129  * Determine some scalers to be able to convert between real time and CPU
130  * ticks. This test uses channel 0 of the PIT (i8254) or the kernel KIP,
131  * depending on availability.
132  * Just calls l4_tsc_init(L4_TSC_INIT_AUTO).
133  */
134 L4_INLINE l4_uint32_t
135 l4_calibrate_tsc (l4_kernel_info_t *kip);
136
137 /**
138  * \brief Initialitze scaler for TSC calicaltions.
139  *
140  * Initialize the scalers needed by l4_tsc_to_ns()/l4_ns_to_tsc() and so on.
141  * Current versions of Fiasco export these scalers from kernel into userland.
142  * The programmer may decide whether he allows to use these scalers or if an
143  * calibration should be performed.
144  * \param   constraint   programmers constraint:
145  *                       - #L4_TSC_INIT_AUTO if the kernel exports the scalers
146  *                         then use them. If not, perform calibration using
147  *                         channel 0 of the PIT (i8254). The latter case may
148  *                         lead into short (unpredictable) periods where
149  *                         interrupts are disabled.
150  *                       - #L4_TSC_INIT_KERNEL depend on retrieving the scalers
151  *                         from kernel. If the scalers are not available,
152  *                         return 0.
153  *                       - #L4_TSC_INIT_CALIBRATE Ignore possible scalers
154  *                         exported by the scaler, instead insist on
155  *                         calibration using the PIT.
156  * \param kip            KIP pointer
157  * \return 0 on error (no scalers exported by kernel, calibrating failed ...)
158  *         otherwise returns (2^32 / (tsc per µsec)). This value has the
159  *         same semantics as the value returned by the calibrate_delay_loop()
160  *         function of the Linux kernel.
161  */
162 L4_CV l4_uint32_t
163 l4_tsc_init (int constraint, l4_kernel_info_t *kip);
164
165 /**
166  * \brief Get CPU frequency in Hz
167  * \return frequency in Hz
168  */
169 L4_CV l4_uint32_t
170 l4_get_hz (void);
171
172 /*@}*/
173
174 EXTERN_C_END
175
176 /* implementaion */
177
178 L4_INLINE l4_uint32_t
179 l4_calibrate_tsc (l4_kernel_info_t *kip)
180 {
181   return l4_tsc_init(L4_TSC_INIT_AUTO, kip);
182 }
183
184 L4_INLINE l4_cpu_time_t
185 l4_rdtsc (void)
186 {
187     l4_cpu_time_t v;
188     
189     __asm__ __volatile__ 
190         (".byte 0x0f, 0x31              \n\t"
191          "mov   $0xffffffff, %%rcx      \n\t" /* clears the upper 32 bits! */
192          "and   %%rcx,%%rax             \n\t"
193          "shlq  $32,%%rdx               \n\t"
194          "orq   %%rdx,%%rax             \n\t"
195         :
196         "=a" (v)
197         : /* no inputs */
198         :"rdx", "rcx"
199         );
200     
201     return v;
202 }
203
204 L4_INLINE l4_cpu_time_t
205 l4_rdpmc (int nr)
206 {
207     l4_cpu_time_t v;
208     l4_uint64_t dummy;
209
210     __asm__ __volatile__ (
211          "rdpmc                         \n\t"
212          "mov   $0xffffffff, %%rcx      \n\t" /* clears the upper 32 bits! */
213          "and   %%rcx,%%rax             \n\t"
214          "shlq  $32,%%rdx               \n\t"
215          "orq   %%rdx,%%rax             \n\t"
216         :
217         "=a" (v), "=c"(dummy)
218         : "c" (nr)
219         : "rdx"
220         );
221
222     return v;
223 }
224
225 /* the same, but only 32 bit. Useful for smaller differences */
226 L4_INLINE
227 l4_uint32_t l4_rdpmc_32(int nr)
228 {
229   l4_uint32_t x;
230   l4_uint64_t dummy;
231
232   __asm__ __volatile__ (
233          "rdpmc                         \n\t"
234          "mov   $0xffffffff, %%rcx      \n\t" /* clears the upper 32 bits! */
235          "and   %%rcx,%%rax             \n\t"
236        : "=a" (x), "=c"(dummy)
237        : "c" (nr)
238        : "rdx");
239
240   return x;
241 }
242
243 /* the same, but only 32 bit. Useful for smaller differences, 
244    needs less cycles. */
245 L4_INLINE 
246 l4_uint32_t l4_rdtsc_32(void)
247 {
248   l4_uint32_t x;
249
250   __asm__ __volatile__ (
251        ".byte 0x0f, 0x31\n\t"   // rdtsc
252        : "=a" (x)
253        :
254        : "rdx");
255     
256   return x;
257 }
258
259 L4_INLINE l4_uint64_t
260 l4_tsc_to_ns (l4_cpu_time_t tsc)
261 {
262     l4_uint64_t ns, dummy;
263     __asm__
264         ("                              \n\t"
265          "mulq  %3                      \n\t"
266          "shrd  $27, %%rdx, %%rax       \n\t"
267         :"=a" (ns), "=d"(dummy)
268         :"a" (tsc), "r" ((l4_uint64_t)l4_scaler_tsc_to_ns)
269         );
270     return ns;
271 }
272
273 L4_INLINE l4_uint64_t
274 l4_tsc_to_us (l4_cpu_time_t tsc)
275 {
276     l4_uint64_t ns, dummy;
277     __asm__
278         ("                              \n\t"
279          "mulq  %3                      \n\t"
280          "shrd  $32, %%rdx, %%rax       \n\t"
281         :"=a" (ns), "=d" (dummy)
282         :"a" (tsc), "r" ((l4_uint64_t)l4_scaler_tsc_to_us)
283         );
284     return ns;
285 }
286
287 L4_INLINE void
288 l4_tsc_to_s_and_ns (l4_cpu_time_t tsc, l4_uint32_t *s, l4_uint32_t *ns)
289 {
290     __asm__
291         ("                              \n\t"
292          "mulq  %3                      \n\t"
293          "shrd  $27, %%rdx, %%rax       \n\t"
294          "xorq  %%rdx, %%rdx            \n\t"
295          "divq  %4                      \n\t"
296         :"=a" (*s), "=&d" (*ns)
297         : "a" (tsc), "r" ((l4_uint64_t)l4_scaler_tsc_to_ns),
298           "rm"(1000000000ULL)
299         );
300 }
301
302 L4_INLINE l4_cpu_time_t
303 l4_ns_to_tsc (l4_uint64_t ns)
304 {
305     l4_uint64_t tsc, dummy;
306     __asm__
307         ("                              \n\t"
308          "mulq  %3                      \n\t"
309          "shrd  $27, %%rdx, %%rax       \n\t"
310         :"=a" (tsc), "=d" (dummy)
311         :"a" (ns), "r" ((l4_uint64_t)l4_scaler_ns_to_tsc)
312         );
313     return tsc;
314 }
315
316 L4_INLINE void
317 l4_busy_wait_ns (l4_uint64_t ns)
318 {
319   l4_cpu_time_t stop = l4_rdtsc();
320   stop += l4_ns_to_tsc(ns);
321
322   while (l4_rdtsc() < stop)
323     ;
324 }
325
326 L4_INLINE void
327 l4_busy_wait_us (l4_uint64_t us)
328 {
329   l4_cpu_time_t stop = l4_rdtsc ();
330   stop += l4_ns_to_tsc(us*1000ULL);
331
332   while (l4_rdtsc() < stop)
333     ;
334 }
335
336 EXTERN_C_END
337
338 #endif /* __l4_rdtsc_h */
339