mirror of
https://github.com/anrieff/libcpuid
synced 2024-11-10 22:59:13 +00:00
parent
ddb8000fd3
commit
1ac6898da0
4 changed files with 116 additions and 0 deletions
|
@ -45,3 +45,4 @@ affinity_mask_str @41
|
||||||
cpuid_free_system_id @42
|
cpuid_free_system_id @42
|
||||||
affinity_mask_str_r @43
|
affinity_mask_str_r @43
|
||||||
cpuid_get_hypervisor @44
|
cpuid_get_hypervisor @44
|
||||||
|
cpu_clock_by_tsc @45
|
||||||
|
|
|
@ -1151,6 +1151,27 @@ int cpu_clock_measure(int millis, int quad_check);
|
||||||
*/
|
*/
|
||||||
int cpu_clock_by_ic(int millis, int runs);
|
int cpu_clock_by_ic(int millis, int runs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Measure the CPU clock frequency using TSC frequency from CPUID
|
||||||
|
*
|
||||||
|
* @param raw - Optional input - a pointer to the raw CPUID data, which is obtained
|
||||||
|
* either by cpuid_get_raw_data or cpuid_deserialize_raw_data.
|
||||||
|
* Can also be NULL, in which case the functions calls
|
||||||
|
* cpuid_get_raw_data itself.
|
||||||
|
*
|
||||||
|
* The function read Time Stamp Counter and Nominal Core Crystal Clock
|
||||||
|
* Information Leaf from CPUID. It determines the processor base frequency.
|
||||||
|
*
|
||||||
|
* NOTE: only x86 Intel CPUs since Skylake (6th generation of Intel Core
|
||||||
|
* processors) are supported. Other vendors do not support this feature.
|
||||||
|
*
|
||||||
|
* @returns the CPU clock frequency in MHz.
|
||||||
|
* If TSC frequency is not supported, the result is -1.
|
||||||
|
* If the input parameters are incorrect, or some other internal fault is
|
||||||
|
* detected, the result is -2.
|
||||||
|
*/
|
||||||
|
int cpu_clock_by_tsc(struct cpu_raw_data_t* raw);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the CPU clock frequency (all-in-one method)
|
* @brief Get the CPU clock frequency (all-in-one method)
|
||||||
*
|
*
|
||||||
|
|
|
@ -42,3 +42,4 @@ affinity_mask_str
|
||||||
cpuid_free_system_id
|
cpuid_free_system_id
|
||||||
affinity_mask_str_r
|
affinity_mask_str_r
|
||||||
cpuid_get_hypervisor
|
cpuid_get_hypervisor
|
||||||
|
cpu_clock_by_tsc
|
||||||
|
|
|
@ -310,6 +310,99 @@ int cpu_clock_by_ic(int millis, int runs)
|
||||||
return max_value;
|
return max_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cpu_clock_by_tsc(struct cpu_raw_data_t* raw)
|
||||||
|
{
|
||||||
|
/* Documentation:
|
||||||
|
* Intel® 64 and IA-32 Architectures Software Developer’s Manual
|
||||||
|
* Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4
|
||||||
|
* 20.7.3 Determining the Processor Base Frequency
|
||||||
|
*/
|
||||||
|
uint16_t base_freq_mhz;
|
||||||
|
uint32_t denominator, numerator, nominal_freq_khz;
|
||||||
|
struct cpu_raw_data_t myraw;
|
||||||
|
struct cpu_id_t id;
|
||||||
|
|
||||||
|
/* Get CPUID raw data and identy CPU */
|
||||||
|
if (!raw) {
|
||||||
|
if (cpuid_get_raw_data(&myraw) < 0) {
|
||||||
|
warnf("cpu_clock_by_tsc: raw CPUID cannot be obtained\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
raw = &myraw;
|
||||||
|
}
|
||||||
|
if (cpu_identify(raw, &id) != ERR_OK) {
|
||||||
|
warnf("cpu_clock_by_tsc: CPU cannot be identified\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if Time Stamp Counter and Nominal Core Crystal Clock Information Leaf is supported */
|
||||||
|
if ((id.vendor != VENDOR_INTEL) || (raw->basic_cpuid[0][EAX] < 0x15)) {
|
||||||
|
debugf(1, "cpu_clock_by_tsc: Time Stamp Counter and Nominal Core Crystal Clock Information Leaf is not supported\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
denominator = raw->basic_cpuid[0x15][EAX]; // Bits 31-00: An unsigned integer which is the denominator of the TSC/”core crystal clock” ratio
|
||||||
|
numerator = raw->basic_cpuid[0x15][EBX]; // Bits 31-00: An unsigned integer which is the numerator of the TSC/”core crystal clock” ratio
|
||||||
|
nominal_freq_khz = raw->basic_cpuid[0x15][ECX] / 1000; // Bits 31-00: An unsigned integer which is the nominal frequency of the core crystal clock in Hz
|
||||||
|
|
||||||
|
/* If EBX[31:0] (numerator) is 0, the TSC/”core crystal clock” ratio is not enumerated. */
|
||||||
|
if ((numerator == 0) || (denominator == 0)) {
|
||||||
|
debugf(1, "cpu_clock_by_tsc: TSC/”core crystal clock” ratio is not enumerated\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If ECX is 0, the nominal core crystal clock frequency is not enumerated.
|
||||||
|
For Intel processors in which CPUID.15H.EBX[31:0] ÷ CPUID.0x15.EAX[31:0] is enumerated but CPUID.15H.ECX
|
||||||
|
is not enumerated, Table 20-91 can be used to look up the nominal core crystal clock frequency. */
|
||||||
|
if ((nominal_freq_khz == 0) && (id.ext_family == 0x6)) {
|
||||||
|
debugf(1, "cpu_clock_by_tsc: nominal core crystal clock frequency is not enumerated, looking for CPUID signature %02X_%02XH\n", id.ext_family, id.ext_model);
|
||||||
|
switch (id.ext_model) {
|
||||||
|
case 0x55:
|
||||||
|
/* Intel® Xeon® Scalable Processor Family with CPUID signature 06_55H */
|
||||||
|
nominal_freq_khz = 25000; // 25 MHz
|
||||||
|
break;
|
||||||
|
case 0x4e:
|
||||||
|
case 0x5e:
|
||||||
|
case 0x8e:
|
||||||
|
case 0x9e:
|
||||||
|
/* 6th and 7th generation Intel® CoreTM processors and Intel® Xeon® W Processor Family */
|
||||||
|
nominal_freq_khz = 24000; // 24 MHz
|
||||||
|
break;
|
||||||
|
case 0x5c:
|
||||||
|
/* Next Generation Intel Atom® processors based on Goldmont Microarchitecture with
|
||||||
|
CPUID signature 06_5CH (does not include Intel Xeon processors) */
|
||||||
|
nominal_freq_khz = 19200; // 19.2 MHz
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* From native_calibrate_tsc() in Linux: https://github.com/torvalds/linux/blob/master/arch/x86/kernel/tsc.c#L696-L707
|
||||||
|
Some Intel SoCs like Skylake and Kabylake don't report the crystal
|
||||||
|
clock, but we can easily calculate it to a high degree of accuracy
|
||||||
|
by considering the crystal ratio and the CPU speed. */
|
||||||
|
if ((nominal_freq_khz == 0) && (raw->basic_cpuid[0][EAX] >= 0x16)) {
|
||||||
|
base_freq_mhz = EXTRACTS_BITS(raw->basic_cpuid[0x16][EAX], 15, 0);
|
||||||
|
nominal_freq_khz = base_freq_mhz * 1000 * denominator / numerator;
|
||||||
|
debugf(1, "cpu_clock_by_tsc: no crystal clock frequency detected, using base frequency (%u MHz) to calculate it\n", base_freq_mhz);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nominal_freq_khz == 0) {
|
||||||
|
debugf(1, "cpu_clock_by_tsc: no crystal clock frequency detected\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For Intel processors in which the nominal core crystal clock frequency is enumerated in CPUID.15H.ECX and the
|
||||||
|
core crystal clock ratio is encoded in CPUID.15H (see Table 3-8 “Information Returned by CPUID Instruction”), the
|
||||||
|
nominal TSC frequency can be determined by using the following equation:
|
||||||
|
Nominal TSC frequency = ( CPUID.15H.ECX[31:0] * CPUID.15H.EBX[31:0] ) ÷ CPUID.15H.EAX[31:0] */
|
||||||
|
debugf(1, "cpu_clock_by_tsc: denominator=%u, numerator=%u, nominal_freq_khz=%u\n", denominator, numerator, nominal_freq_khz);
|
||||||
|
|
||||||
|
/* Return TSC frequency in MHz */
|
||||||
|
return (nominal_freq_khz * numerator) / denominator / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
int cpu_clock(void)
|
int cpu_clock(void)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
|
|
Loading…
Reference in a new issue