如何更改结构的字节顺序?

How can I change the byte orders of a struct?

如何在 C 或 C++ 中更改压缩结构的顺序?

struct myStruct {
  uint32_t A;
  uint16_t B1;
  uint16_t B2;
} __attribute__((packed));

结构(或 LSB)的地址 0x0 是 A

我的应用程序与硬件通信,硬件中的结构定义如下:

struct packed {
  logic [31:0] A;
  logic [15:0] B1;
  logic [15:0] B2;
} myStruct;

但在 SystemVerilog 中,“地址 0x0”或更准确地说结构的 LSB 是 LSB of B2 = B2[0]

顺序颠倒了。

为了保持一致并避免更改硬件部分,我想反转整个 C/C++ 结构的“字节顺序”。

我可以反转所有字段:

struct myStruct {
  uint16_t B2;
  uint16_t B1;
  uint32_t A;
} __attribute__((packed));

但是很容易出错,不太方便

对于数据类型,SystemVerilog 和 Intel CPU 都是小端,这不是问题。

How can I change the byte orders of a struct?

您不能更改成员中的字节顺序。并且您不能将成员相对于其他成员的内存顺序更改为不同于它们声明的顺序。

但是,您可以更改成员的声明顺序,这决定了它们的内存顺序。第一个成员总是在内存的最低位置,第二个在后面,依此类推。

如果可以根据 verilog 源知道成员的正确顺序,那么理想情况下,应该使用 meta-programming 生成 C 结构定义以确保匹配顺序。

it's error-prone

依赖于特定的内存顺序确实是error-prone。

完全不依赖成员的内存顺序,而只依赖源数据(大概是一个字节数组)的已知内存顺序是可能的:

unsigned char* data = read_hardware();
myStruct s;
s.B2 = data[0] << 0u
     | data[1] << 8u;
s.B1 = data[2] << 0u
     | data[3] << 8u;
s.A  = data[4] << 0u
     | data[5] << 8u
     | data[6] << 16u
     | data[7] << 24u;

这既不依赖于成员的内存布局,也不依赖于 CPU 的字节顺序。它仅依赖于源数据的顺序(在这种情况下假定为小端)。

如果可能的话,这个函数最好也是基于verilog源码​​生成的。

How can I change the order of a packed structure in C or C++?

C 指定结构体的成员按照声明的顺序在内存中布局,当转换为适当的指针类型,等于整个结构的地址。至少对于可在 C 中表达的结构类型,例如您的结构类型,符合 C++ 的实现将遵循相同的 member-order 规则。那些支持打包结构布局作为扩展的实现在它们的意思上非常一致:打包结构布局在成员之间没有填充,整体大小是成员大小的总和。而且没有其他影响。

我不知道有任何实现提供扩展,允许成员以不同于声明顺序的方式排序,谁会费心去实现它?成员的顺序是well-defined。如果你想要不同的顺序,那么解决办法就是改变成员的声明顺序。

如果 VeriLog 确实对成员进行了不同的排序(对此我不能说),那么我认为您只需要接受它。根据需要或其他最有意义的方式实施它,记录双方,然后继续。我倾向于认为,注意到两种语言的声明顺序不同的人会很少。只要存在适当的文档,那些注意到的人就不会倾向于认为有错误。

你知道我刚刚看过 AMD 在它的开源驱动程序中处理字节顺序。

首先,他们使用 cmake 检测系统是否大 endian/little 字节序。

#if !defined (__GFX10_GB_REG_H__)
#define __GFX10_GB_REG_H__

/*
*    gfx10_gb_reg.h
*
*    Register Spec Release:  1.0
*
*/

//
// Make sure the necessary endian defines are there.
//
#if defined(LITTLEENDIAN_CPU)
#elif defined(BIGENDIAN_CPU)
#else
#error "BIGENDIAN_CPU or LITTLEENDIAN_CPU must be defined"
#endif

union GB_ADDR_CONFIG
{
    struct
    {
#if defined(LITTLEENDIAN_CPU)
        unsigned int                       NUM_PIPES : 3;
        unsigned int            PIPE_INTERLEAVE_SIZE : 3;
        unsigned int            MAX_COMPRESSED_FRAGS : 2;
        unsigned int                       NUM_PKRS  : 3;
        unsigned int                                 : 21;
#elif defined(BIGENDIAN_CPU)
        unsigned int                                 : 21;
        unsigned int                       NUM_PKRS  : 3;
        unsigned int            MAX_COMPRESSED_FRAGS : 2;
        unsigned int            PIPE_INTERLEAVE_SIZE : 3;
        unsigned int                       NUM_PIPES : 3;
#endif
    } bitfields, bits;
    unsigned int    u32All;
    int             i32All;
    float           f32All;
};

#endif

是的,如上所述,存在一些代码重复。但我也不知道有一个普遍更好的解决方案。