Endianness macros

N

Noob

[ 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.

<quote>

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) << 8 ) | \
(((Value) & 0x000000FF) << 24)); \
}
#else /* big endian CPU */
#define FOOBA_WriteRegMem32BE(Address_p, Value) \
{ \
*((FOOBA_MU32 *) (Address_p)) = (U32) (Value); \
}
#endif /* _BIG_NOT_LITTLE */
#else /* not optimized */
#define FOOBA_WriteRegMem32BE(Address_p, Value) \
{ \
*(((FOOBA_MU8 *) (Address_p)) ) = (U8) ((Value) >> 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 ) | \
(((Value) & 0x000000FF) << 24)); \
}
#endif /* _BIG_NOT_LITTLE */
#else /* not optimized */
#define FOOBA_WriteRegMem32LE(Address_p, Value) \
{ \
*(((FOOBA_MU8 *) (Address_p)) ) = (U8) ((Value) ); \
*(((FOOBA_MU8 *) (Address_p)) + 1) = (U8) ((Value) >> 8 ); \
*(((FOOBA_MU8 *) (Address_p)) + 2) = (U8) ((Value) >> 16); \
*(((FOOBA_MU8 *) (Address_p)) + 3) = (U8) ((Value) >> 24); \
}
#endif /* _NO_OPTIMIZATION */


</quote>

(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.
 
B

Ben Bacarisse

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

<quote>

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) << 8 ) | \
(((Value) & 0x000000FF) << 24)); \
}
#else /* big endian CPU */
#define FOOBA_WriteRegMem32BE(Address_p, Value) \
{ \
*((FOOBA_MU32 *) (Address_p)) = (U32) (Value); \
}
#endif /* _BIG_NOT_LITTLE */
#else /* not optimized */
#define FOOBA_WriteRegMem32BE(Address_p, Value) \
{ \
*(((FOOBA_MU8 *) (Address_p)) ) = (U8) ((Value) >> 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 */
</quote>

(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?

Yes, one doe not usually have to care.
I'd just write:

FOOBA_WriteRegMem32(addr, 259);

In fact, one normally writes *addr = 259; because addr will have the
As far as I understand, endianness only matters when considering a
number's representation, not when considering a number's value?
Yup.

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

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

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.
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.)

No, that does not seem to be the purpose.
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?

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.
 
L

larwe

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

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.
 
M

Mel

larwe said:
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.

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.
 
B

Boudewijn Dijkstra

[snip]
</quote>

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

Totally irrelevant.
Suppose I want to write the *value* 259 to address "addr"
259(base 10) = 0x00000103(base 16)

I assume that the target address is external to the MCU.
I shouldn't have to care whether the data is stored least-significant
octet first or most-significant octet first, right?

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.
[...]

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.)

Only if that different platform has its external bus wired or configured
differently for the address space you're talking to.
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?

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top