]>
Commit | Line | Data |
---|---|---|
c7de829c WD |
1 | /**************************************************************************** |
2 | * | |
3 | * SciTech OS Portability Manager Library | |
4 | * | |
5 | * ======================================================================== | |
6 | * | |
7 | * The contents of this file are subject to the SciTech MGL Public | |
8 | * License Version 1.0 (the "License"); you may not use this file | |
9 | * except in compliance with the License. You may obtain a copy of | |
10 | * the License at http://www.scitechsoft.com/mgl-license.txt | |
11 | * | |
12 | * Software distributed under the License is distributed on an | |
13 | * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | |
14 | * implied. See the License for the specific language governing | |
15 | * rights and limitations under the License. | |
16 | * | |
17 | * The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc. | |
18 | * | |
19 | * The Initial Developer of the Original Code is SciTech Software, Inc. | |
20 | * All Rights Reserved. | |
21 | * | |
22 | * ======================================================================== | |
23 | * | |
24 | * Language: ANSI C | |
25 | * Environment: Any | |
26 | * | |
27 | * Description: Main module to implement the Zen Timer support functions. | |
28 | * | |
29 | ****************************************************************************/ | |
30 | ||
31 | #include "ztimer.h" | |
32 | #include "pmapi.h" | |
33 | #include "oshdr.h" | |
34 | #if !defined(__WIN32_VXD__) && !defined(__OS2_VDD__) && !defined(__NT_DRIVER__) | |
35 | #include <stdio.h> | |
36 | #include <string.h> | |
37 | #endif | |
38 | ||
39 | /*----------------------------- Implementation ----------------------------*/ | |
40 | ||
41 | /* External Intel assembler functions */ | |
42 | #ifdef __INTEL__ | |
43 | /* {secret} */ | |
44 | ibool _ASMAPI _CPU_haveCPUID(void); | |
45 | /* {secret} */ | |
46 | ibool _ASMAPI _CPU_check80386(void); | |
47 | /* {secret} */ | |
48 | ibool _ASMAPI _CPU_check80486(void); | |
49 | /* {secret} */ | |
50 | uint _ASMAPI _CPU_checkCPUID(void); | |
51 | /* {secret} */ | |
52 | uint _ASMAPI _CPU_getCPUIDModel(void); | |
53 | /* {secret} */ | |
54 | uint _ASMAPI _CPU_getCPUIDStepping(void); | |
55 | /* {secret} */ | |
56 | uint _ASMAPI _CPU_getCPUIDFeatures(void); | |
57 | /* {secret} */ | |
58 | uint _ASMAPI _CPU_getCacheSize(void); | |
59 | /* {secret} */ | |
60 | uint _ASMAPI _CPU_have3DNow(void); | |
61 | /* {secret} */ | |
62 | ibool _ASMAPI _CPU_checkClone(void); | |
63 | /* {secret} */ | |
64 | void _ASMAPI _CPU_readTimeStamp(CPU_largeInteger *time); | |
65 | /* {secret} */ | |
66 | void _ASMAPI _CPU_runBSFLoop(ulong iterations); | |
67 | /* {secret} */ | |
68 | ulong _ASMAPI _CPU_mulDiv(ulong a,ulong b,ulong c); | |
69 | /* {secret} */ | |
70 | void ZTimerQuickInit(void); | |
71 | #define CPU_HaveMMX 0x00800000 | |
72 | #define CPU_HaveRDTSC 0x00000010 | |
73 | #define CPU_HaveSSE 0x02000000 | |
74 | #endif | |
75 | ||
76 | #if defined(__SMX32__) | |
77 | #include "smx/cpuinfo.c" | |
78 | #elif defined(__RTTARGET__) | |
79 | #include "rttarget/cpuinfo.c" | |
80 | #elif defined(__REALDOS__) | |
81 | #include "dos/cpuinfo.c" | |
82 | #elif defined(__NT_DRIVER__) | |
83 | #include "ntdrv/cpuinfo.c" | |
84 | #elif defined(__WIN32_VXD__) | |
85 | #include "vxd/cpuinfo.c" | |
86 | #elif defined(__WINDOWS32__) | |
87 | #include "win32/cpuinfo.c" | |
88 | #elif defined(__OS2_VDD__) | |
89 | #include "vdd/cpuinfo.c" | |
90 | #elif defined(__OS2__) | |
91 | #include "os2/cpuinfo.c" | |
92 | #elif defined(__LINUX__) | |
93 | #include "linux/cpuinfo.c" | |
94 | #elif defined(__QNX__) | |
95 | #include "qnx/cpuinfo.c" | |
96 | #elif defined(__BEOS__) | |
97 | #include "beos/cpuinfo.c" | |
98 | #else | |
99 | #error CPU library not ported to this platform yet! | |
100 | #endif | |
101 | ||
102 | /*------------------------ Public interface routines ----------------------*/ | |
103 | ||
104 | /**************************************************************************** | |
105 | REMARKS: | |
106 | Read an I/O port location. | |
107 | ****************************************************************************/ | |
108 | static uchar rdinx( | |
109 | int port, | |
110 | int index) | |
111 | { | |
112 | PM_outpb(port,(uchar)index); | |
113 | return PM_inpb(port+1); | |
114 | } | |
115 | ||
116 | /**************************************************************************** | |
117 | REMARKS: | |
118 | Write an I/O port location. | |
119 | ****************************************************************************/ | |
120 | static void wrinx( | |
121 | ushort port, | |
122 | ushort index, | |
123 | ushort value) | |
124 | { | |
125 | PM_outpb(port,(uchar)index); | |
126 | PM_outpb(port+1,(uchar)value); | |
127 | } | |
128 | ||
129 | /**************************************************************************** | |
130 | REMARKS: | |
131 | Enables the Cyrix CPUID instruction to properly detect MediaGX and 6x86 | |
132 | processors. | |
133 | ****************************************************************************/ | |
134 | static void _CPU_enableCyrixCPUID(void) | |
135 | { | |
136 | uchar ccr3; | |
137 | ||
138 | PM_init(); | |
139 | ccr3 = rdinx(0x22,0xC3); | |
140 | wrinx(0x22,0xC3,(uchar)(ccr3 | 0x10)); | |
141 | wrinx(0x22,0xE8,(uchar)(rdinx(0x22,0xE8) | 0x80)); | |
142 | wrinx(0x22,0xC3,ccr3); | |
143 | } | |
144 | ||
145 | /**************************************************************************** | |
146 | DESCRIPTION: | |
147 | Returns the type of processor in the system. | |
148 | ||
149 | HEADER: | |
150 | ztimer.h | |
151 | ||
152 | RETURNS: | |
153 | Numerical identifier for the installed processor | |
154 | ||
155 | REMARKS: | |
156 | Returns the type of processor in the system. Note that if the CPU is an | |
157 | unknown Pentium family processor that we don't have an enumeration for, | |
158 | the return value will be greater than or equal to the value of CPU_UnkPentium | |
159 | (depending on the value returned by the CPUID instruction). | |
160 | ||
161 | SEE ALSO: | |
162 | CPU_getProcessorSpeed, CPU_haveMMX, CPU_getProcessorName | |
163 | ****************************************************************************/ | |
164 | uint ZAPI CPU_getProcessorType(void) | |
165 | { | |
166 | #if defined(__INTEL__) | |
167 | uint cpu,vendor,model,cacheSize; | |
168 | static ibool firstTime = true; | |
169 | ||
170 | if (_CPU_haveCPUID()) { | |
171 | cpu = _CPU_checkCPUID(); | |
172 | vendor = cpu & ~CPU_mask; | |
173 | if (vendor == CPU_Intel) { | |
174 | /* Check for Intel processors */ | |
175 | switch (cpu & CPU_mask) { | |
176 | case 4: cpu = CPU_i486; break; | |
177 | case 5: cpu = CPU_Pentium; break; | |
178 | case 6: | |
179 | if ((model = _CPU_getCPUIDModel()) == 1) | |
180 | cpu = CPU_PentiumPro; | |
181 | else if (model <= 6) { | |
182 | cacheSize = _CPU_getCacheSize(); | |
183 | if ((model == 5 && cacheSize == 0) || | |
184 | (model == 5 && cacheSize == 256) || | |
185 | (model == 6 && cacheSize == 128)) | |
186 | cpu = CPU_Celeron; | |
187 | else | |
188 | cpu = CPU_PentiumII; | |
189 | } | |
190 | else if (model >= 7) { | |
191 | /* Model 7 == Pentium III */ | |
192 | /* Model 8 == Celeron/Pentium III Coppermine */ | |
193 | cacheSize = _CPU_getCacheSize(); | |
194 | if ((model == 8 && cacheSize == 128)) | |
195 | cpu = CPU_Celeron; | |
196 | else | |
197 | cpu = CPU_PentiumIII; | |
198 | } | |
199 | break; | |
200 | default: | |
201 | cpu = CPU_UnkIntel; | |
202 | } | |
203 | } | |
204 | else if (vendor == CPU_Cyrix) { | |
205 | /* Check for Cyrix processors */ | |
206 | switch (cpu & CPU_mask) { | |
207 | case 4: | |
208 | if ((model = _CPU_getCPUIDModel()) == 4) | |
209 | cpu = CPU_CyrixMediaGX; | |
210 | else | |
211 | cpu = CPU_UnkCyrix; | |
212 | break; | |
213 | case 5: | |
214 | if ((model = _CPU_getCPUIDModel()) == 2) | |
215 | cpu = CPU_Cyrix6x86; | |
216 | else if (model == 4) | |
217 | cpu = CPU_CyrixMediaGXm; | |
218 | else | |
219 | cpu = CPU_UnkCyrix; | |
220 | break; | |
221 | case 6: | |
222 | if ((model = _CPU_getCPUIDModel()) <= 1) | |
223 | cpu = CPU_Cyrix6x86MX; | |
224 | else | |
225 | cpu = CPU_UnkCyrix; | |
226 | break; | |
227 | default: | |
228 | cpu = CPU_UnkCyrix; | |
229 | } | |
230 | } | |
231 | else if (vendor == CPU_AMD) { | |
232 | /* Check for AMD processors */ | |
233 | switch (cpu & CPU_mask) { | |
234 | case 4: | |
235 | if ((model = _CPU_getCPUIDModel()) == 0) | |
236 | cpu = CPU_AMDAm5x86; | |
237 | else | |
238 | cpu = CPU_AMDAm486; | |
239 | break; | |
240 | case 5: | |
241 | if ((model = _CPU_getCPUIDModel()) <= 3) | |
242 | cpu = CPU_AMDK5; | |
243 | else if (model <= 7) | |
244 | cpu = CPU_AMDK6; | |
245 | else if (model == 8) | |
246 | cpu = CPU_AMDK6_2; | |
247 | else if (model == 9) | |
248 | cpu = CPU_AMDK6_III; | |
249 | else if (model == 13) { | |
250 | if (_CPU_getCPUIDStepping() <= 3) | |
251 | cpu = CPU_AMDK6_IIIplus; | |
252 | else | |
253 | cpu = CPU_AMDK6_2plus; | |
254 | } | |
255 | else | |
256 | cpu = CPU_UnkAMD; | |
257 | break; | |
258 | case 6: | |
259 | if ((model = _CPU_getCPUIDModel()) == 3) | |
260 | cpu = CPU_AMDDuron; | |
261 | else | |
262 | cpu = CPU_AMDAthlon; | |
263 | break; | |
264 | default: | |
265 | cpu = CPU_UnkAMD; | |
266 | } | |
267 | } | |
268 | else if (vendor == CPU_IDT) { | |
269 | /* Check for IDT WinChip processors */ | |
270 | switch (cpu & CPU_mask) { | |
271 | case 5: | |
272 | if ((model = _CPU_getCPUIDModel()) <= 4) | |
273 | cpu = CPU_WinChipC6; | |
274 | else if (model == 8) | |
275 | cpu = CPU_WinChip2; | |
276 | else | |
277 | cpu = CPU_UnkIDT; | |
278 | break; | |
279 | default: | |
280 | cpu = CPU_UnkIDT; | |
281 | } | |
282 | } | |
283 | else { | |
284 | /* Assume a Pentium compatible Intel clone */ | |
285 | cpu = CPU_Pentium; | |
286 | } | |
287 | return cpu | vendor | (_CPU_getCPUIDStepping() << CPU_steppingShift); | |
288 | } | |
289 | else { | |
290 | if (_CPU_check80386()) | |
291 | cpu = CPU_i386; | |
292 | else if (_CPU_check80486()) { | |
293 | /* If we get here we may have a Cyrix processor so we can try | |
294 | * enabling the CPUID instruction and trying again. | |
295 | */ | |
296 | if (firstTime) { | |
297 | firstTime = false; | |
298 | _CPU_enableCyrixCPUID(); | |
299 | return CPU_getProcessorType(); | |
300 | } | |
301 | cpu = CPU_i486; | |
302 | } | |
303 | else | |
304 | cpu = CPU_Pentium; | |
305 | if (!_CPU_checkClone()) | |
306 | return cpu | CPU_Intel; | |
307 | return cpu; | |
308 | } | |
309 | #elif defined(__ALPHA__) | |
310 | return CPU_Alpha; | |
311 | #elif defined(__MIPS__) | |
312 | return CPU_Mips; | |
313 | #elif defined(__PPC__) | |
314 | return CPU_PowerPC; | |
315 | #endif | |
316 | } | |
317 | ||
318 | /**************************************************************************** | |
319 | DESCRIPTION: | |
320 | Returns true if the processor supports Intel MMX extensions. | |
321 | ||
322 | HEADER: | |
323 | ztimer.h | |
324 | ||
325 | RETURNS: | |
326 | True if MMX is available, false if not. | |
327 | ||
328 | REMARKS: | |
329 | This function determines if the processor supports the Intel MMX extended | |
330 | instruction set. | |
331 | ||
332 | SEE ALSO: | |
333 | CPU_getProcessorType, CPU_getProcessorSpeed, CPU_have3DNow, CPU_haveSSE, | |
334 | CPU_getProcessorName | |
335 | ****************************************************************************/ | |
336 | ibool ZAPI CPU_haveMMX(void) | |
337 | { | |
338 | #ifdef __INTEL__ | |
339 | if (_CPU_haveCPUID()) | |
340 | return (_CPU_getCPUIDFeatures() & CPU_HaveMMX) != 0; | |
341 | return false; | |
342 | #else | |
343 | return false; | |
344 | #endif | |
345 | } | |
346 | ||
347 | /**************************************************************************** | |
348 | DESCRIPTION: | |
349 | Returns true if the processor supports AMD 3DNow! extensions. | |
350 | ||
351 | HEADER: | |
352 | ztimer.h | |
353 | ||
354 | RETURNS: | |
355 | True if 3DNow! is available, false if not. | |
356 | ||
357 | REMARKS: | |
358 | This function determines if the processor supports the AMD 3DNow! extended | |
359 | instruction set. | |
360 | ||
361 | SEE ALSO: | |
362 | CPU_getProcessorType, CPU_getProcessorSpeed, CPU_haveMMX, CPU_haveSSE, | |
363 | CPU_getProcessorName | |
364 | ****************************************************************************/ | |
365 | ibool ZAPI CPU_have3DNow(void) | |
366 | { | |
367 | #ifdef __INTEL__ | |
368 | if (_CPU_haveCPUID()) | |
369 | return _CPU_have3DNow(); | |
370 | return false; | |
371 | #else | |
372 | return false; | |
373 | #endif | |
374 | } | |
375 | ||
376 | /**************************************************************************** | |
377 | DESCRIPTION: | |
378 | Returns true if the processor supports Intel KNI extensions. | |
379 | ||
380 | HEADER: | |
381 | ztimer.h | |
382 | ||
383 | RETURNS: | |
384 | True if Intel KNI is available, false if not. | |
385 | ||
386 | REMARKS: | |
387 | This function determines if the processor supports the Intel KNI extended | |
388 | instruction set. | |
389 | ||
390 | SEE ALSO: | |
391 | CPU_getProcessorType, CPU_getProcessorSpeed, CPU_haveMMX, CPU_have3DNow, | |
392 | CPU_getProcessorName | |
393 | ****************************************************************************/ | |
394 | ibool ZAPI CPU_haveSSE(void) | |
395 | { | |
396 | #ifdef __INTEL__ | |
397 | if (_CPU_haveCPUID()) | |
398 | return (_CPU_getCPUIDFeatures() & CPU_HaveSSE) != 0; | |
399 | return false; | |
400 | #else | |
401 | return false; | |
402 | #endif | |
403 | } | |
404 | ||
405 | /**************************************************************************** | |
406 | RETURNS: | |
407 | True if the RTSC instruction is available, false if not. | |
408 | ||
409 | REMARKS: | |
410 | This function determines if the processor supports the Intel RDTSC | |
411 | instruction, for high precision timing. If the processor is not an Intel or | |
412 | Intel clone CPU, this function will always return false. | |
413 | ||
414 | DESCRIPTION: | |
415 | Returns true if the processor supports RDTSC extensions. | |
416 | ||
417 | HEADER: | |
418 | ztimer.h | |
419 | ||
420 | RETURNS: | |
421 | True if RTSC is available, false if not. | |
422 | ||
423 | REMARKS: | |
424 | This function determines if the processor supports the RDTSC instruction | |
425 | for reading the processor time stamp counter. | |
426 | ||
427 | SEE ALSO: | |
428 | CPU_getProcessorType, CPU_getProcessorSpeed, CPU_haveMMX, CPU_have3DNow, | |
429 | CPU_getProcessorName | |
430 | ****************************************************************************/ | |
431 | ibool ZAPI CPU_haveRDTSC(void) | |
432 | { | |
433 | #ifdef __INTEL__ | |
434 | if (_CPU_haveCPUID()) | |
435 | return (_CPU_getCPUIDFeatures() & CPU_HaveRDTSC) != 0; | |
436 | return false; | |
437 | #else | |
438 | return false; | |
439 | #endif | |
440 | } | |
441 | ||
442 | #ifdef __INTEL__ | |
443 | ||
444 | #define ITERATIONS 16000 | |
445 | #define SAMPLINGS 2 | |
446 | #define INNER_LOOPS 400 | |
447 | ||
448 | /**************************************************************************** | |
449 | REMARKS: | |
450 | If processor does not support time stamp reading, but is at least a 386 or | |
451 | above, utilize method of timing a loop of BSF instructions which take a | |
452 | known number of cycles to run on i386(tm), i486(tm), and Pentium(R) | |
453 | processors. | |
454 | ****************************************************************************/ | |
455 | static ulong GetBSFCpuSpeed( | |
456 | ulong cycles) | |
457 | { | |
458 | CPU_largeInteger t0,t1,count_freq; | |
459 | ulong ticks; /* Microseconds elapsed during test */ | |
460 | ulong current; /* Variable to store time elapsed */ | |
461 | int i,j,iPriority; | |
462 | ulong lowest = (ulong)-1; | |
463 | ||
464 | iPriority = SetMaxThreadPriority(); | |
465 | GetCounterFrequency(&count_freq); | |
466 | for (i = 0; i < SAMPLINGS; i++) { | |
467 | GetCounter(&t0); | |
468 | for (j = 0; j < INNER_LOOPS; j++) | |
469 | _CPU_runBSFLoop(ITERATIONS); | |
470 | GetCounter(&t1); | |
471 | current = t1.low - t0.low; | |
472 | if (current < lowest) | |
473 | lowest = current; | |
474 | } | |
475 | RestoreThreadPriority(iPriority); | |
476 | ||
477 | /* Compute frequency */ | |
478 | ticks = _CPU_mulDiv(lowest,1000000,count_freq.low); | |
479 | if ((ticks % count_freq.low) > (count_freq.low/2)) | |
480 | ticks++; /* Round up if necessary */ | |
481 | if (ticks == 0) | |
482 | return 0; | |
483 | return ((cycles*INNER_LOOPS)/ticks); | |
484 | } | |
485 | ||
486 | #define TOLERANCE 1 | |
487 | ||
488 | /**************************************************************************** | |
489 | REMARKS: | |
490 | On processors supporting the Read Time Stamp opcode, compare elapsed | |
491 | time on the High-Resolution Counter with elapsed cycles on the Time | |
492 | Stamp Register. | |
493 | ||
494 | The inner loop runs up to 20 times oruntil the average of the previous | |
495 | three calculated frequencies is within 1 MHz of each of the individual | |
496 | calculated frequencies. This resampling increases the accuracy of the | |
497 | results since outside factors could affect this calculation. | |
498 | ****************************************************************************/ | |
499 | static ulong GetRDTSCCpuSpeed( | |
500 | ibool accurate) | |
501 | { | |
502 | CPU_largeInteger t0,t1,s0,s1,count_freq; | |
503 | u64 stamp0, stamp1, ticks0, ticks1; | |
504 | u64 total_cycles, cycles, hz, freq; | |
505 | u64 total_ticks, ticks; | |
506 | int tries,iPriority; | |
507 | ulong maxCount; | |
508 | ||
509 | PM_set64_32(total_cycles,0); | |
510 | PM_set64_32(total_ticks,0); | |
511 | maxCount = accurate ? 600000 : 30000; | |
512 | iPriority = SetMaxThreadPriority(); | |
513 | GetCounterFrequency(&count_freq); | |
514 | PM_set64(freq,count_freq.high,count_freq.low); | |
515 | for (tries = 0; tries < 3; tries++) { | |
516 | /* Loop until 100 ticks have passed since last read of hi-res | |
517 | * counter. This accounts for overhead later. | |
518 | */ | |
519 | GetCounter(&t0); | |
520 | t1.low = t0.low; | |
521 | t1.high = t0.high; | |
522 | while ((t1.low - t0.low) < 100) { | |
523 | GetCounter(&t1); | |
524 | _CPU_readTimeStamp(&s0); | |
525 | } | |
526 | ||
527 | /* Loop until 30000 ticks have passed since last read of hi-res counter. | |
528 | * This allows for elapsed time for sampling. For a hi-res frequency | |
529 | * of 1MHz, this is about 0.03 of a second. The frequency reported | |
530 | * by the OS dependent code should be tuned to provide a good | |
531 | * sample period depending on the accuracy of the OS timers (ie: | |
532 | * if the accuracy is lower, lower the frequency to spend more time | |
533 | * in the inner loop to get better accuracy). | |
534 | */ | |
535 | t0.low = t1.low; | |
536 | t0.high = t1.high; | |
537 | while ((t1.low - t0.low) < maxCount) { | |
538 | GetCounter(&t1); | |
539 | _CPU_readTimeStamp(&s1); | |
540 | } | |
541 | ||
542 | /* Find the difference during the timing loop */ | |
543 | PM_set64(stamp0,s0.high,s0.low); | |
544 | PM_set64(stamp1,s1.high,s1.low); | |
545 | PM_set64(ticks0,t0.high,t0.low); | |
546 | PM_set64(ticks1,t1.high,t1.low); | |
547 | PM_sub64(cycles,stamp1,stamp0); | |
548 | PM_sub64(ticks,ticks1,ticks0); | |
549 | ||
550 | /* Sum up the results */ | |
551 | PM_add64(total_ticks,total_ticks,ticks); | |
552 | PM_add64(total_cycles,total_cycles,cycles); | |
553 | } | |
554 | RestoreThreadPriority(iPriority); | |
555 | ||
556 | /* Compute frequency in Hz */ | |
557 | PM_mul64(hz,total_cycles,freq); | |
558 | PM_div64(hz,hz,total_ticks); | |
559 | return PM_64to32(hz); | |
560 | } | |
561 | ||
562 | #endif /* __INTEL__ */ | |
563 | ||
564 | /**************************************************************************** | |
565 | DESCRIPTION: | |
566 | Returns the speed of the processor in MHz. | |
567 | ||
568 | HEADER: | |
569 | ztimer.h | |
570 | ||
571 | PARAMETERS: | |
572 | accurate - True of the speed should be measured accurately | |
573 | ||
574 | RETURNS: | |
575 | Processor speed in MHz. | |
576 | ||
577 | REMARKS: | |
578 | This function returns the speed of the CPU in MHz. Note that if the speed | |
579 | cannot be determined, this function will return 0. | |
580 | ||
581 | If the accurate parameter is set to true, this function will spend longer | |
582 | profiling the speed of the CPU, and will not round the CPU speed that is | |
583 | reported. This is important for highly accurate timing using the Pentium | |
584 | RDTSC instruction, but it does take a lot longer for the profiling to | |
585 | produce accurate results. | |
586 | ||
587 | SEE ALSO: | |
588 | CPU_getProcessorSpeedInHz, CPU_getProcessorType, CPU_haveMMX, | |
589 | CPU_getProcessorName | |
590 | ****************************************************************************/ | |
591 | ulong ZAPI CPU_getProcessorSpeed( | |
592 | ibool accurate) | |
593 | { | |
594 | #if defined(__INTEL__) | |
595 | /* Number of cycles needed to execute a single BSF instruction on i386+ | |
596 | * processors. | |
597 | */ | |
598 | ulong cpuSpeed; | |
599 | uint i; | |
600 | static ulong intel_cycles[] = { | |
601 | 115,47,43, | |
602 | }; | |
603 | static ulong cyrix_cycles[] = { | |
604 | 38,38,52,52, | |
605 | }; | |
606 | static ulong amd_cycles[] = { | |
607 | 49, | |
608 | }; | |
609 | static ulong known_speeds[] = { | |
610 | 1000,950,900,850,800,750,700,650,600,550,500,450,433,400,350, | |
611 | 333,300,266,233,200,166,150,133,120,100,90,75,66,60,50,33,20,0, | |
612 | }; | |
613 | ||
614 | if (CPU_haveRDTSC()) { | |
615 | cpuSpeed = (GetRDTSCCpuSpeed(accurate) + 500000) / 1000000; | |
616 | } | |
617 | else { | |
618 | int type = CPU_getProcessorType(); | |
619 | int processor = type & CPU_mask; | |
620 | int vendor = type & CPU_familyMask; | |
621 | if (vendor == CPU_Intel) | |
622 | cpuSpeed = GetBSFCpuSpeed(ITERATIONS * intel_cycles[processor - CPU_i386]); | |
623 | else if (vendor == CPU_Cyrix) | |
624 | cpuSpeed = GetBSFCpuSpeed(ITERATIONS * cyrix_cycles[processor - CPU_Cyrix6x86]); | |
625 | else if (vendor == CPU_AMD) | |
626 | cpuSpeed = GetBSFCpuSpeed(ITERATIONS * amd_cycles[0]); | |
627 | else | |
628 | return 0; | |
629 | } | |
630 | ||
631 | /* Now normalise the results given known processors speeds, if the | |
632 | * speed we measure is within 2MHz of the expected values | |
633 | */ | |
634 | if (!accurate) { | |
635 | for (i = 0; known_speeds[i] != 0; i++) { | |
636 | if (cpuSpeed >= (known_speeds[i]-3) && cpuSpeed <= (known_speeds[i]+3)) { | |
637 | return known_speeds[i]; | |
638 | } | |
639 | } | |
640 | } | |
641 | return cpuSpeed; | |
642 | #else | |
643 | return 0; | |
644 | #endif | |
645 | } | |
646 | ||
647 | /**************************************************************************** | |
648 | DESCRIPTION: | |
649 | Returns the speed of the processor in Hz. | |
650 | ||
651 | HEADER: | |
652 | ztimer.h | |
653 | ||
654 | RETURNS: | |
655 | Accurate processor speed in Hz. | |
656 | ||
657 | REMARKS: | |
658 | This function returns the accurate speed of the CPU in Hz. Note that if the | |
659 | speed cannot be determined, this function will return 0. | |
660 | ||
661 | This function is similar to the CPU_getProcessorSpeed function, except that | |
662 | it attempts to accurately measure the CPU speed in Hz. This is used | |
663 | internally in the Zen Timer libraries to provide accurate real world timing | |
664 | information. This is important for highly accurate timing using the Pentium | |
665 | RDTSC instruction, but it does take a lot longer for the profiling to | |
666 | produce accurate results. | |
667 | ||
668 | SEE ALSO: | |
669 | CPU_getProcessorSpeed, CPU_getProcessorType, CPU_haveMMX, | |
670 | CPU_getProcessorName | |
671 | ****************************************************************************/ | |
672 | ulong ZAPI CPU_getProcessorSpeedInHZ( | |
673 | ibool accurate) | |
674 | { | |
675 | #if defined(__INTEL__) | |
676 | if (CPU_haveRDTSC()) { | |
677 | return GetRDTSCCpuSpeed(accurate); | |
678 | } | |
679 | return CPU_getProcessorSpeed(false) * 1000000; | |
680 | #else | |
681 | return 0; | |
682 | #endif | |
683 | } | |
684 | ||
685 | /**************************************************************************** | |
686 | DESCRIPTION: | |
687 | Returns a string defining the speed and name of the processor. | |
688 | ||
689 | HEADER: | |
690 | ztimer.h | |
691 | ||
692 | RETURNS: | |
693 | Processor name string. | |
694 | ||
695 | REMARKS: | |
696 | This function returns an English string describing the speed and name of the | |
697 | CPU. | |
698 | ||
699 | SEE ALSO: | |
700 | CPU_getProcessorType, CPU_haveMMX, CPU_getProcessorName | |
701 | ****************************************************************************/ | |
702 | char * ZAPI CPU_getProcessorName(void) | |
703 | { | |
704 | #if defined(__INTEL__) | |
705 | static int cpu,speed = -1; | |
706 | static char name[80]; | |
707 | ||
708 | if (speed == -1) { | |
709 | cpu = CPU_getProcessorType(); | |
710 | speed = CPU_getProcessorSpeed(false); | |
711 | } | |
712 | sprintf(name,"%d MHz ", speed); | |
713 | switch (cpu & CPU_mask) { | |
714 | case CPU_i386: | |
715 | strcat(name,"Intel i386 processor"); | |
716 | break; | |
717 | case CPU_i486: | |
718 | strcat(name,"Intel i486 processor"); | |
719 | break; | |
720 | case CPU_Pentium: | |
721 | strcat(name,"Intel Pentium processor"); | |
722 | break; | |
723 | case CPU_PentiumPro: | |
724 | strcat(name,"Intel Pentium Pro processor"); | |
725 | break; | |
726 | case CPU_PentiumII: | |
727 | strcat(name,"Intel Pentium II processor"); | |
728 | break; | |
729 | case CPU_Celeron: | |
730 | strcat(name,"Intel Celeron processor"); | |
731 | break; | |
732 | case CPU_PentiumIII: | |
733 | strcat(name,"Intel Pentium III processor"); | |
734 | break; | |
735 | case CPU_UnkIntel: | |
736 | strcat(name,"Unknown Intel processor"); | |
737 | break; | |
738 | case CPU_Cyrix6x86: | |
739 | strcat(name,"Cyrix 6x86 processor"); | |
740 | break; | |
741 | case CPU_Cyrix6x86MX: | |
742 | strcat(name,"Cyrix 6x86MX processor"); | |
743 | break; | |
744 | case CPU_CyrixMediaGX: | |
745 | strcat(name,"Cyrix MediaGX processor"); | |
746 | break; | |
747 | case CPU_CyrixMediaGXm: | |
748 | strcat(name,"Cyrix MediaGXm processor"); | |
749 | break; | |
750 | case CPU_UnkCyrix: | |
751 | strcat(name,"Unknown Cyrix processor"); | |
752 | break; | |
753 | case CPU_AMDAm486: | |
754 | strcat(name,"AMD Am486 processor"); | |
755 | break; | |
756 | case CPU_AMDAm5x86: | |
757 | strcat(name,"AMD Am5x86 processor"); | |
758 | break; | |
759 | case CPU_AMDK5: | |
760 | strcat(name,"AMD K5 processor"); | |
761 | break; | |
762 | case CPU_AMDK6: | |
763 | strcat(name,"AMD K6 processor"); | |
764 | break; | |
765 | case CPU_AMDK6_2: | |
766 | strcat(name,"AMD K6-2 processor"); | |
767 | break; | |
768 | case CPU_AMDK6_III: | |
769 | strcat(name,"AMD K6-III processor"); | |
770 | break; | |
771 | case CPU_AMDK6_2plus: | |
772 | strcat(name,"AMD K6-2+ processor"); | |
773 | break; | |
774 | case CPU_AMDK6_IIIplus: | |
775 | strcat(name,"AMD K6-III+ processor"); | |
776 | break; | |
777 | case CPU_UnkAMD: | |
778 | strcat(name,"Unknown AMD processor"); | |
779 | break; | |
780 | case CPU_AMDAthlon: | |
781 | strcat(name,"AMD Athlon processor"); | |
782 | break; | |
783 | case CPU_AMDDuron: | |
784 | strcat(name,"AMD Duron processor"); | |
785 | break; | |
786 | case CPU_WinChipC6: | |
787 | strcat(name,"IDT WinChip C6 processor"); | |
788 | break; | |
789 | case CPU_WinChip2: | |
790 | strcat(name,"IDT WinChip 2 processor"); | |
791 | break; | |
792 | case CPU_UnkIDT: | |
793 | strcat(name,"Unknown IDT processor"); | |
794 | break; | |
795 | default: | |
796 | strcat(name,"Unknown processor"); | |
797 | } | |
798 | if (CPU_haveMMX()) | |
799 | strcat(name," with MMX(R)"); | |
800 | if (CPU_have3DNow()) | |
801 | strcat(name,", 3DNow!(R)"); | |
802 | if (CPU_haveSSE()) | |
803 | strcat(name,", SSE(R)"); | |
804 | return name; | |
805 | #else | |
806 | return "Unknown"; | |
807 | #endif | |
808 | } |