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