views:

152

answers:

7

I have to program peripheral registers in an ARM9-based microcontroller.

For instance, for the USART, I store the relevant memory addresses in an enum:

enum USART
{
    US_BASE = (int) 0xFFFC4000,
    US_BRGR = US_BASE + 0x16,
    //...
};

Then, I use pointers in a function to initialize the registers:

void init_usart (void)
{
    vuint* pBRGR = (vuint*) US_BRGR;
    *pBRGR = 0x030C;
    //...
}

But my teacher says I'd better use #defines, such as:

#define US_BASE (0xFFFC4000)
#define US_BRGR (US_BASE + 0x16)
#define pBRGR   ((vuint*) US_BRGR)

void init_usart (void)
{
    *pBRGR = 0x030C;
}

Like so, he says, you don't have the overhead of allocating pointers in the stack.

Personally, I don't like #defines much, nor other preprocessor directives. So the question is, in this particular case, are #defines really worth using instead of enums and stack-allocated pointers ?


Related question: Want to configure a particular peripheral register in ARM9 based chip

A: 

I wouldn't necessarily say that either way is better. It is just personal preference. As for your professor's argument, it is really a moot point. Allocating variables on the stack is one instruction, no matter how many there are, usually in the form sub esp, 10h. So if you have one local or 20, it is still one instruction to allocate the space for all of them.

I would say that the one advantage of the #include is that if for some reason down the road you wanted to change how that pointer is accessed, you just need to change it in one location.

Mario
A: 

I would tend towards using an enum, for potential future compatibility with C++ code. I say this because at my job, we have a lot of C header files shared between projects, some of which use C code and some of which use C++. For those using C++, we'd often like to wrap the definitions in a namespace, to prevent symbol masking, but you can't assign a #define to a namespace.

Caleb Huitt - cjhuitt
+4  A: 

I am afraid that enum are a dead end for such a task. The standard defines enum constants to be of type int, so in general they are not compatible with pointers.

One day on an architecture with 32bit int and 64bit pointers you might have a constant that doesn't fit into an int. It is not well defined what will happen.

On the other hand the argument that enum would allocate something on the stack is not valid. They are compile time constants and have nothing to do with the function stack or no more than any constants that you specify through macros.

Jens Gustedt
The `#define` versions of `US_BASE` and `US_BRGR` are also ints...
Michael Burr
@Michael: but that can be fixed by adding `UL` at the end.
Potatoswatter
+1  A: 

In my experience, one big reason to use #define for this kind of thing is that it's more of the standard idiom used in the embedded community.

Using enums instead of #define will generate questions/comments from instructors (and in the future, colleagues), even when using other techniques might have other advantages (like not stomping on the global identifier namespace).

I personally like using enums for numeric constants, but sometimes you need to do what is customary for what and where you're working.

However, performance shouldn't be an issue.

Michael Burr
+11  A: 

The approach I've always preferred is to first define a struct reflecting the peripherals register layout

typedef volatile unsigned int reg32; // or other appropriate 32-bit interger type
typedef struct USART
{
    reg32 pad1;
    reg32 pad2;
    reg32 pad3;
    reg32 pad4;
    reg32 brgr;
    // any other registers
} USART;

USART *p_usart0 = (USART * const) 0xFFFC4000;

Then in code I can just use

p_usart0->brgr = 0x030C;

This approach is much cleaner when you have multiple instances of the same sort of peripheral:

USART *p_usart1 = (USART * const) 0xFFFC5000;
USART *p_usart2 = (USART * const) 0xFFFC6000;

User sbass provided a link to an excellent column by Dan Saks that gives much more detail on this technique, and points out its advantages over other approaches.

If you're lucky enough to be using C++, then you can add methods for all the common operations on the peripheral and nicely encapsulate the devices peculiarities.

Stephen C. Steel
This is much better. TI uses this method for their procesors (at least, for 28xx DSPs and MSP430). The method for locating registers is a little different; it sticks them in particular sections and uses the linker file to locate them at a specific address.
Jason S
Also you should have the whole structure be volatile, not just the individual components.
Jason S
@Jason, If all the registers are declared volatile, then declaring the entire struct volatile won't make any difference. In either case, the compiler is not allowed to optimise away any explicit reads or writes to the registers.
Stephen C. Steel
@Jason, Stephen: volatiles are often miscompiled: http://www.cs.utah.edu/~regehr/papers/emsoft08-preprint.pdf And specifically, even on newest version of gcc and clang, volatile structs are miscompiled: http://blog.regehr.org/archives/274
pkh
@pkh: !!!!!!!?!
Jason S
@pkh Thanks for the references. If I could edit comments, I should change my reply to Jason from "declaring the entire struct volatile won't make any difference" to "declaring the entire struct volatile shouldn't make any difference".
Stephen C. Steel
+1  A: 

The answer is always do whatever the teacher wants and pass the class then on your own question everything and find out if their reasons were valid and form your own opinions. You cant win against the school, not worth it.

In this case it is easy to compile to assembler or disassemble to see the difference if any between the enum and define.

I would recommend the define over enum, have had compiler discomfort with enums. I highly discourage using pointers the way you are using them, I have seen every compiler fail to accurately generate the desired instructions, it is rare but when it happens you will wonder how your last decades of coding ever worked. Pointing structs or anything else is considerably worse. I often get flamed for this, and expect to this time around. Too many miles around the block, fixed too much broken code with these problems to ignore the root cause.

dwelch
+4  A: 

Dan Saks has written a number of columns on this for Embedded Systems Programming. Here's one of his latest ones. He discusses C, C++, enums, defines, structs, classes, etc. and why you might one over another. Definitely worth reading and always good advice.

sbass