在c中使用struct进行位字段提取

时间:2014-06-06 19:59:56

标签: c struct bit-manipulation intel cpu-registers

我使用这两种方法从寄存器中获取位字段信息。我需要提取的位字段的位置由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

2 个答案:

答案 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%确定它完全正确。通过调试器,检查所有的班次组合。绝对确定你没事。

当测试程序正常工作时,将功能转移到真实程序,并在第一时间观察它。

我想我可以为你编写这个功能,但我不认为我会。你真的需要经历这个练习,这样你就可以了解它的工作原理和原因。