Ian*_*han 2 c c++ bit bit-fields
我试图通过PCI总线读取低于32位的读取到VME桥接芯片(Tundra Universe II),然后将其转移到VME总线并由目标接收.
目标VME应用程序仅接受D32(数据宽度读取为32位)并将忽略其他任何内容.
如果我使用在VME窗口上映射的位字段结构(nmap'd到主存储器中),我可以读取> 24位的位字段,但是任何不足都会失败.即: -
struct works {
unsigned int a:24;
};
struct fails {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct main {
works work;
fails fail;
}
volatile *reg = function_that_creates_and_maps_the_vme_windows_returns_address()
Run Code Online (Sandbox Code Playgroud)
这表明,结构工程,读为32位,但一读通过失败的结构一对如REG-> fail.a是越来越因素下读取X位.(其中X可能是16或8?)
所以问题是:
a)这缩小了哪里?编译器?OS?还是Tundra芯片?
b)执行的读操作的实际大小是多少?
我基本上想排除芯片以外的一切.关于它的文档是在网上,但如果可以证明通过PCI总线请求的数据宽度是32位,那么问题可归咎于Tundra芯片!
编辑: -
具体的例子,代码是: -
struct SVersion
{
unsigned title : 8;
unsigned pecversion : 8;
unsigned majorversion : 8;
unsigned minorversion : 8;
} Version;
Run Code Online (Sandbox Code Playgroud)
所以现在我把它改成了: -
union UPECVersion
{
struct SVersion
{
unsigned title : 8;
unsigned pecversion : 8;
unsigned majorversion : 8;
unsigned minorversion : 8;
} Version;
unsigned int dummy;
};
Run Code Online (Sandbox Code Playgroud)
和基本主要结构: -
typedef struct SEPUMap
{
...
...
UPECVersion PECVersion;
};
Run Code Online (Sandbox Code Playgroud)
所以我仍然需要更改所有基线代码
// perform dummy 32bit read
pEpuMap->PECVersion.dummy;
// get the bits out
x = pEpuMap->PECVersion.Version.minorversion;
Run Code Online (Sandbox Code Playgroud)
我怎么知道第二次读取是否真的再次真正读取,就像我的原始代码那样?(而不是通过联合使用已读取的位!)
您的编译器正在将结构的大小调整为其内存对齐设置的倍数.几乎所有现代编译器都这样做.在某些处理器上,变量和指令必须从内存地址开始,这些内存地址是某些内存对齐值的倍数(通常为32位或64位,但对齐取决于处理器体系结构).大多数现代处理器不再需要内存对齐 - 但几乎所有处理器都看到了大量的性能优势.因此,编译器会为您的数据调整以提高性能.
但是,在许多情况下(例如你的),这不是你想要的行为.由于各种原因,您的结构大小可能变得非常重要.在这些情况下,有各种方法解决问题.
一种选择是强制编译器使用不同的对齐设置.执行此操作的选项因编译器而异,因此您必须检查文档.它通常是某种#pragma.在某些编译器(例如Microsoft编译器)上,可以仅为非常小的代码段更改内存对齐.例如(在VC++中):
#pragma pack(push) // save the current alignment
#pragma pack(1) // set the alignment to one byte
// Define variables that are alignment sensitive
#pragma pack(pop) // restore the alignment
Run Code Online (Sandbox Code Playgroud)
另一种选择是以其他方式定义变量.内部类型不会根据对齐进行调整,因此,不是24位位域,而是另一种方法是将变量定义为字节数组.
最后,您可以让编译器根据需要制作结构,并手动记录读/写所需的大小.只要你没有将结构连接在一起,这应该可以正常工作.但是请记住,编译器会在底层给你填充结构,所以如果你创建一个更大的结构,包括一个作品和一个失败的结构,它们之间会有填充位,这可能会导致你的问题.
在大多数编译器中,创建小于8位的数据类型几乎是不可能的.大多数架构都不这么认为.这应该不是一个大问题,因为大多数使用小于8位数据类型的硬件设备最终会以这样的方式安排它们的数据包,它们仍然是8位多路复用,所以你可以进行位操作来提取或在数据流离开或进入时对其进行编码.
由于上面列出的所有原因,许多适用于此类硬件设备的代码都使用原始字节数组,只是对数组中的数据进行编码.尽管失去了现代语言结构的许多便利,但它最终变得更容易.