我使用这两种方法从寄存器中获取位字段信息。我需要提取的位字段的位置由Intel Manual提供。就像下面的代码一样。但是我得到的结果与这两种方法不同。
我找不到这两种方法的任何问题。但据我了解,maximum_power提交不应该是' 0' 0作为第一种方法(这是英特尔已在寄存器中定义的值。)
方法1:
typedef struct rapl_parameters_msr_t {
uint64_t thermal_spec_power : 15;
uint64_t : 1;
uint64_t minimum_power : 15;
uint64_t : 1;
uint64_t maximum_power : 15;
uint64_t : 1;
uint64_t maximum_limit_time_window : 6;
uint64_t : 10;
} rapl_parameters_msr_t;
uint64_t msr;
read_msr(cpu, 0x614, &msr);
rapl_parameters_msr_t domain_msr = *(rapl_parameters_msr_t *)&msr;
printf("%ld\n", domain_msr.thermal_spec_power); //print: 280
printf("%ld\n", domain_msr.minimum_power); //print: 192
printf("%ld\n", domain_msr.maximum_power); //print: 0
printf("%ld\n", domain_msr.maximum_limit_time_window); //print: 16
方法2:
uint64_t
extractBitField(uint64_t inField, uint64_t width, uint64_t offset)
{
uint64_t bitMask;
uint64_t outField;
if ((offset+width) == 32)
{
bitMask = (0xFFFFFFFF<<offset);
}
else
{ /*Just keep the filed needs to be extrated*/
bitMask = (0xFFFFFFFF<<offset) ^ (0xFFFFFFFF<<(offset+width));
}
/*Move to the right most field to be calculated*/
outField = (inField & bitMask) >> offset;
return outField;
}
uint64_t flags;
read_msr(cpu, 0x614, &flags);
printf("thermal power: %d\n", extractBitField(flags,15,0)); //print: 280
printf("minimum power: %d\n", extractBitField(flags,15,16));//print: 192
printf("maximum power: %d\n", extractBitField(flags,15,32));//print: 0
printf("time window: %d\n", extractBitField(flags,6,48)); //print: 0
你有什么见解可以解决问题吗?
更新
对不起困惑的部分。我将所有类型更改为uint64_t,方法2的最大功率和时间窗口都为0 ..
如果编译器会对方法1产生错误的结果,我仍然怀疑我对方法2的结果有多信任。
以下是英特尔手册中的位代表文档:
Thermal Spec Power (bits 14:0)
Minimum Power (bits 30:16)
Maximum Power (bits 46:32)
Maximum Time Window (bits 53:48)
感谢大卫,这是64位提取的正确版本。
uint64_t
extractBitField(uint64_t inField, uint64_t width, uint64_t offset)
{
uint64_t bitMask;
uint64_t outField;
if ((offset+width) == 64)
{
bitMask = (0xFFFFFFFFFFFFFFFF<<offset);
}
else
{ /*Just keep the filed needs to be extrated*/
bitMask = (0xFFFFFFFFFFFFFFFF<<offset) ^ (0xFFFFFFFFFFFFFFFF<<(offset+width));
}
/*Move to the right most field to be calculated*/
outField = (inField & bitMask) >> offset;
return outField;
}
uint64_t flags;
read_msr(cpu, 0x614, &flags);
printf("thermal power: %d\n", extractBitField(flags,15,0)); //print: 280
printf("minimum power: %d\n", extractBitField(flags,15,16));//print: 192
printf("maximum power: %d\n", extractBitField(flags,15,32));//print: 0
printf("time window: %d\n", extractBitField(flags,6,48)); //print: 16
答案 0 :(得分:5)
C位域中位的排序是实现定义的,所以如果你打算使用它们要小心 - 你认为你得到的顺序可能不是你实际的。查看编译器的文档,了解它是如何处理的。
此外,第二个函数接受uint32,而第一个示例使用64位结构,因此您的类型不匹配。你能纠正这个并更新你的结果吗?
编辑:此外,您在第一个示例中将时间窗口定义为六位,在第二个示例中定义为15位。
C99:6.7.2.1p10实现可以分配任何可寻址存储 单位大到足以容纳一个位域。如果有足够的空间,a 紧跟在结构中另一个位字段之后的位字段 应被包装到同一单元的相邻位中。如果不足 空间仍然存在,是否有一个不合适的位场被放入 下一个单元或重叠相邻单元是实现定义的。该 单位内位域分配的顺序(高位到 实现定义的是低阶或低阶到高阶。该 未指定可寻址存储单元的对齐。
答案 1 :(得分:1)
你已经尝试过两种方法来做同样的事情,我不会相信他们中的任何一种。
首先是位字段。 不要使用它们!位字段的排序是不可靠的,除unsigned int之外的任何行为都是不可靠的,跨结构成员的位字段分布是不可靠的。所有这些都可以修复,但它不值得。
第二,转移和掩码。这是正确的方式,但代码错误。你有一个32位掩码(0xffffffff)移位32和48位。根本不是一个好主意。
因此,您需要做的是编写一个简单可靠的函数,该函数是给定签名的实现。
extractBitField(uint64_t inField, uint64_t width, uint64_t offset)
这是一个很好的起点。将该函数写入测试程序并对其进行单元测试,直到您100%确定它完全正确。通过调试器,检查所有的班次组合。绝对确定你没事。
当测试程序正常工作时,将功能转移到真实程序,并在第一时间观察它。
我想我可以为你编写这个功能,但我不认为我会。你真的需要经历这个练习,这样你就可以了解它的工作原理和原因。