Endianness macros

[ NB: cross-posted to comp.lang.c and comp.arch.embedded ]

Hello everyone,

I'm having trouble understanding some endianness-related macros provided on my platform.

typedef volatile U32 FOOBA_MU32; typedef volatile U16 FOOBA_MU16; typedef volatile U8 FOOBA_MU8;

/* ------------------------------------------------------- */ /* void FOOBA_WriteRegMem32BE(void *Address_p, U32 Value); */ /* ------------------------------------------------------- */ #ifndef FOOBA_MEMORY_ACCESS_NO_OPTIMIZATION /* optimized */ #ifndef FOOBA_MEMORY_ACCESS_BIG_NOT_LITTLE /* little endian CPU */ #define FOOBA_WriteRegMem32BE(Address_p, Value) \ { \ *((FOOBA_MU32 *) (Address_p)) = (U32) ((((Value) & 0xFF000000) >> 24) | \ (((Value) & 0x00FF0000) >> 8 ) | \ (((Value) & 0x0000FF00) 24); \ *(((FOOBA_MU8 *) (Address_p)) + 1) = (U8) ((Value) >> 16); \ *(((FOOBA_MU8 *) (Address_p)) + 2) = (U8) ((Value) >> 8 ); \ *(((FOOBA_MU8 *) (Address_p)) + 3) = (U8) ((Value) ); \ } #endif /* _NO_OPTIMIZATION */

/* ------------------------------------------------------- */ /* void FOOBA_WriteRegMem32LE(void *Address_p, U32 Value); */ /* ------------------------------------------------------- */ #ifndef FOOBA_MEMORY_ACCESS_NO_OPTIMIZATION /* optimized */ #ifndef FOOBA_MEMORY_ACCESS_BIG_NOT_LITTLE /* little endian CPU */ #define FOOBA_WriteRegMem32LE(Address_p, Value) \ { \ *((FOOBA_MU32 *) (Address_p)) = (U32) (Value); \ } #else /* big endian CPU */ #define FOOBA_WriteRegMem32LE(Address_p, Value) \ { \ *((FOOBA_MU32 *) (Address_p)) = (U32) ((((Value) & 0xFF000000) >> 24) | \ (((Value) & 0x00FF0000) >> 8 ) | \ (((Value) & 0x0000FF00) 8 ); \ *(((FOOBA_MU8 *) (Address_p)) + 2) = (U8) ((Value) >> 16); \ *(((FOOBA_MU8 *) (Address_p)) + 3) = (U8) ((Value) >> 24); \ } #endif /* _NO_OPTIMIZATION */

(As far as I understand, registers are memory-mapped, but this should not matter (or does it?) for this discussion.)

Suppose I want to write the *value* 259 to address "addr"

259(base 10) = 0x00000103(base 16)

I shouldn't have to care whether the data is stored least-significant octet first or most-significant octet first, right?

I'd just write:

FOOBA_WriteRegMem32(addr, 259);

As far as I understand, endianness only matters when considering a number's representation, not when considering a number's value?

Whether a system is big-endian, little-endian, or weird-endian, value & 0xff gives the number's least-significant octet, right?

So, in my case, should I write FOOBA_WriteRegMem32LE(addr, 259); or FOOBA_WriteRegMem32BE(addr, 259); ??

And if I change to a different platform, do I have to change all my calls? (That would not make sense, I must've missed something.)

...

I've been discussing this issue with a colleague, and he suggested that perhaps many differing components' registers may be mapped in the address space, some big-endian, other little-endian, thus the programmer must know what kind of register he is accessing. Would that be a plausible explanation?

Regards.

Reply to
Noob
Loading thread data ...

| \

| \

Yes, one doe not usually have to care.

In fact, one normally writes *addr = 259; because addr will have the right type (pointer to volatile ). There is no obvious advantage in wrapping it in a macro.

Yup.

Yup.

I'd want to know why you have write either. These macros did not just appear -- they must be part of some other code that you are using to solve some problem. Knowing why they were written, might make their purpose clearer, but it must have something to do with writing data that is not "native". Something in what you are doing would seem to need writing data in a format that specified not by the machine's addressing but by something else.

No, that does not seem to be the purpose.

That sounds more like it. It's hard to say with so little information, but there seems to be a need to write data in a representation that must be explicit in the program and which may or may not be the native format for the machine. Some file formats are like that, for example, as are many protocols.

--
Ben.
Reply to
Ben Bacarisse

erhaps

some

Reading the sample code, it seems very likely that you've got a bus that runs in one particular endianness (say PCI) and a processor core that lives in the opposite endianness, and you are trying to keep your internal representation of the contents of these device registers consistent with the datasheets.

Reply to
larwe

Also very common when you've got a buffer full of binary stuff read in from some other medium -- file, Internet packet, EEPROM, etc. -- which was created with a different convention from yours.

Mel.

Reply to
Mel

Op Fri, 27 Nov 2009 13:18:02 +0100 schreef Noob :

Totally irrelevant.

I assume that the target address is external to the MCU.

It depends. If the data bus you're talking to is 8 bits, then it might matter, depending on whether your MCU has an appropriately configured external bus controller peripheral. If the data bus is 32 bits, then you should only care if it isn't wired correctly. For a 16-bit bus, both considerations apply.

Only if that different platform has its external bus wired or configured differently for the address space you're talking to.

Yes. But changing a component's endianness in hardware is usually very easy, so I guess this is for a low-cost device.

--
Gemaakt met Opera's revolutionaire e-mailprogramma:  
http://www.opera.com/mail/
 Click to see the full signature
Reply to
Boudewijn Dijkstra

[snip]

Actually, I suspect that is a big part of the problem! It implies that the "device" (addresses) being accessed is external to the processor itself (you haven't mentioned specifics -- processor, device, etc.).

A Little Endian processor stores LSB at lower address and MSB at next higher address (I make no claims yet about the TIME SEQUENCE ORDERING in which this occurs -- MSB, LSB vs. LSB, MSB). The reverse is true for Big Endian processors.

(pretend we're just dealing with a 16 bit value, here. And, that the data bus is 8 bits wide. You can extend the concept to wider words and busses)

If the device you are talking to occupies multiple (consecutive) addresses, then the relationship of LSB & MSB to address X & address X+1 will differ depending on the Endianess of the processor.

For example, assume you have a UART with data register at X and control at X+1. The value 0x3100 would write an ASCII '1' (0x31) to the data register (causing the character to be transmitted, for example) and a "00" to the control register FOR A BIG ENDIAN processor.

OTOH, you would end up writing a 0x00 to the data register and a 0x31 to the control register in a Little Endian architecture. Quite different results. :>

This is often encountered in network drivers where the data is sent in "network byte order" (which must be "universal") but must be interpreted in *host* byte order.

Reply to
D Yuniskis

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.