In most scenarios, endianness is automatically handled by the compiler. However some use cases arise where it cannot be ignored. For example, when sending serialized data over a bus.
In the example below, 0x1234 must be sent over a little endian bus. Sending data directly like below only works on a little endian target:
uint16_tvalue=0x1234;/* Only works on little endian target. */send_data_over_le_bus(&value);/* Byte by byte. */
Unfortunately, a common solution is to manually swap bytes in order to port the code over to a big endian target. However this reverses the issue. Now code only works for a big endian target:
uint16_tvalue=0x1234;/* Hard-coded to only work for big endian targets. */value=((value&0xFF)<<8)|((value&0xFF00)>>8);send_data_over_le_bus(&value);/* Byte by byte. */
This is unportable. If the target ever changes back to a little endian machine, every byte swapping operation scattered throughout the codebase would have to be manually changed.
The macros in this module provide a portable solution by selectively swapping bytes based on the target’s endianness. In this case, bytes are only swapped when compiling on a big endian machine. Otherwise the macro does nothing:
/* Portable. Works on both little and big endian targets. */uint16_tvalue=ECU_CPU_TO_LE16_RUNTIME(0x1234);send_data_over_le_bus(&value);/* Byte by byte. */
The endianness of the target must be known so the macros explained in the previous section expand to the proper operations. ECU_LITTLE_ENDIAN must be defined for a little endian target orECU_BIG_ENDIAN must be defined for a big endian target.
ECU attempts to detect the endianness of the target and automatically define either ECU_LITTLE_ENDIAN or ECU_BIG_ENDIAN by using the CMake variable CMAKE_C_BYTE_ORDER. If this is unsuccessful, a warning message will be printed in the console at configuration time. A compilation error will also occur if this module is used in this case:
If auto-detection fails or a CMake build system is not being used, the endianness of the target can be manually specified by passing -DECU_LITTLE_ENDIANor-DECU_BIG_ENDIAN compiler flags.
The following macros provide a portable way to read big endian encoded data. Reading data directly like in the example below only works on a big endian target:
uint16_tvalue;read_be_data(&value);/* memcpy. *//* Only works on big endian target. */if(value==0x1234){do_stuff();}
Manually swapping bytes to make the code work for a little endian target reverses the issue. The code now only works for a little endian target:
uint16_tvalue;read_be_data(&value);/* memcpy. *//* Hard-coded to only work on little endian target. */value=((value&0xFF)<<8)|((value&0xFF00)>>8);if(value==0x1234){do_stuff();}
These macros provide a portable solution by selectively swapping bytes based on the target’s endianness. Bytes are only swapped when compiling on a little endian machine. Otherwise these macros do nothing:
uint16_tvalue;read_be_data(&value);/* memcpy. *//* Portable. Works on both little and big endian targets. */if(ECU_BE16_TO_CPU_RUNTIME(value)==0x1234){do_stuff();}
The following macros provide a portable way to read little endian encoded data. Exact same logic as ECU_BE_TO_CPU() macros but for little endian data. Bytes are only swapped when compiling on a big endian machine. Otherwise these macros do nothing and return the same input.
The following macros provide a portable way to send data in big endian format. Sending data directly like in the example below only works on a big endian target:
uint16_tvalue=0x1234;/* Only works on big endian target. */send_data_over_be_bus(&value);/* Byte by byte. */
Manually swapping bytes to make the code work for a little endian target reverses the issue. The code now only works for a little endian target:
uint16_tvalue=0x1234;/* Hard-coded to only work for little endian targets. */value=((value&0xFF)<<8)|((value&0xFF00)>>8);send_data_over_be_bus(&value);/* Byte by byte. */
These macros provide a portable solution by selectively swapping bytes based on the target’s endianness. Bytes are only swapped when compiling on a little endian machine. Otherwise these macros do nothing:
/* Portable. Works on both little and big endian targets. */uint16_tvalue=ECU_CPU_TO_BE16_RUNTIME(0x1234);send_data_over_be_bus(&value);/* Byte by byte. */
The following macros provide a portable way to send data in little endian format. Exact same logic as ECU_CPU_TO_BE() macros but for sending little endian data. Bytes are only swapped when compiling on a big endian machine. Otherwise these macros do nothing and return the same input.