-1

I am doing some embedded software on a 32 bits architecture (precisely AVR32). On this software I am retrieving a flag, coded on one byte, from an external peripheral connected via I2C.

Each bits of this byte are a boolean variable. The struct is the following:

typedef union __attribute__((packed)){
  struct __attribute__((packed)) {
    uint8_t flag_a : 1;
    uint8_t flag_b : 1;
    uint8_t flag_c : 1;
    uint8_t flag_d : 1;
    uint8_t flag_e : 1;
    uint8_t __unused : 3;
  }
  uint8_t raw;
}Flag_t;

My question is, if I want to make this code portable, do I need to redefine the struct inside the union in the reverse order for the other endianness ?

Or, is the endianness issues not present in this situation ?

Amaury
  • 73
  • 7
  • 2
    If you want to make code porting easier, then [don't use bit-fields.](https://stackoverflow.com/a/55825057/694733) – user694733 Jun 01 '23 at 07:50
  • @user694733 I am using GCC and a proprietary compiler that have a lot of things in common with GCC. I also use a library that is used by a broad variety of architectures and therefore compilers, and they uses bit-field as well (but on several bytes structs). So I figured it was acceptable to work with bit-fields in embedded. – Amaury Jun 01 '23 at 08:03
  • 2
    Side note: Don't use identifiers such as `__unused`. Identifiers starting with double underscore are reserved, and may cause problem when porting code to new compiler or newer C standard. – user694733 Jun 01 '23 at 08:16
  • 1
    @Amaury - I think it is the connection to "an external peripheral" that triggers the comments. The standard doesn't say if `flag_a` is the first or last bit of the byte, and it is known that other compilers do the opposite of gcc. – BoP Jun 01 '23 at 09:06
  • 1
    There's absolutely no reason to use a bit-field for this. – Lundin Jun 01 '23 at 10:04
  • 2
    This will not even compile, `flag_a` can't be used multiple times in the same bitfield. – 12431234123412341234123 Jun 01 '23 at 10:09
  • Also, if you want to make your code portable, you can't use the `_t` suffix on your types. [Identifiers that end in `_t` are reserved by POSIX](https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/V2_chap02.html#tag_15_02_02) – Andrew Henle Jun 01 '23 at 11:48
  • @12431234123412341234123 Sorry I made a mistake while writing the example union, of course the previous could not compile. – Amaury Jun 05 '23 at 07:11
  • @Amaury Simple solution to avoid this problem: Compile your code before posting. – 12431234123412341234123 Jun 07 '23 at 09:48

2 Answers2

3

Endianness is only an issue for multi-byte variables, so it is not an issue in your case.

However, the use of bit-fields is potentially an issue for portability. The C Standard leaves it up to the compiler implementation to decide how they are implemented. Thus they cannot be expected to behave identically across different environments.

A better option is simply to use a single byte and define the meaning of the different bits. E.g.:

   typedef uint8_t Flag_t;
   typedef enum
   {
      flag_a = 1u << 0,
      flag_b = 1u << 1,
      flag_c = 1u << 2,
      flag d = 1u << 3,
      flag e = 1u << 4,
   } Flag_Bits_t;
nielsen
  • 5,641
  • 10
  • 27
  • Thank you ! That is indeed a very interesting way of doing it. However several bits of Flag_t may be enabled at the same time. This makes it difficult to test the enum value in a switch. – Amaury Jun 01 '23 at 08:06
  • 3
    I would use `#define` instead of enums for bit constants. Enum constants cannot be unsigned type (although it's not an issue in this particular case). – user694733 Jun 01 '23 at 08:09
  • @user694733 You are right and I might also prefer `#define`. The compiled code will probably end up being the same in most cases, but with `#define` there may less risk of getting a warning in some cases. – nielsen Jun 01 '23 at 08:21
  • 1
    @Amaury You are welcome. Testing for which flags are set is somewhat independent of this question. It is probably better to use `if/else` as in e.g. `if(flags & (flag_a | flag_b)) ...`. – nielsen Jun 01 '23 at 08:29
  • 1
    nielsen, "Endianness is only an issue for multi-byte variables" --> Endianness is also a bit ordering issue in select case such as if `uint8_t __unused : 3;` is the 3 most or least significant bits. – chux - Reinstate Monica Jun 01 '23 at 13:41
  • 1
    @chux-ReinstateMonica Yes, but that is part of the implementation-defined behavior of bit fields contributing to why they are not being portable. From a C standard point of view, bits are identified by their value. Thus "the bit corresponding to the value 32" is a portable way of identifying what is commonly referred to as the 5th bit in a byte. Of course, bit order matters when bytes are serialized into bits and deserialized, but that is a responsibility of the hardware/driver to ensure that this is done correctly for the given system. – nielsen Jun 01 '23 at 14:17
3

if I want to make this code portable

Then do not use bit-fields. Use bit operations.

in the reverse order for the other endianness ?

Yes, but this is compiler specific, i.e. not portable. On gcc you can use Bitfield endianness in gcc .

is the endianness issues not present in this situation ?

Yes, but not only - compiler may insert any padding between structure members, uint8_t may not be supported for a bitfield, and it is "endianness" of bits within a byte that compiler chooses to make.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111