views:

565

answers:

6

I'm writing system-level code for an embedded system without memory protection (on an ARM Cortex-M1, compiling with gcc 4.3) and need to read/write directly to a memory-mapped register. So far, my code looks like this:

#define UART0     0x4000C000
#define UART0CTL  (UART0 + 0x30)

volatile unsigned int *p;
p = UART0CTL;
*p &= ~1;

Is there any shorter way (shorter in code, I mean) that does not use a pointer? I looking for a way to write the actual assignment code as short as this (it would be okay if I had to use more #defines):

*(UART0CTL) &= ~1;

Anything I tried so far ended up with gcc complaining that it could not assign something to the lvalue...

+15  A: 
#define UART0CTL ((volatile unsigned int *) (UART0 + 0x30))

:-P

Edited to add: Oh, in response to all the comments about how the question is tagged C++ as well as C, here's a C++ solution. :-P

inline unsigned volatile& uart0ctl() {
    return *reinterpret_cast<unsigned volatile*>(UART0 + 0x30);
}

This can be stuck straight in a header file, just like the C-style macro, but you have to use function call syntax to invoke it.

Chris Jester-Young
cool, that works, thanks. I still seem to miss the finer points of C/C++ ...
maligree
@maligree: Casting is one of the fundamental operations in C, definitely not a "finer point". :-P (This isn't so much a poke at you, as the fact that C seems to require lots of casting to get Real Work done. :-P)
Chris Jester-Young
Sure, but I meant the art of putting it all together into a single, but still "human readable" line. With your solution, IMO I ended up with beautiful code whose semantics could be seen in an instant (well, okay, I still could #define UARTDISABLE ~1 to add more semantics to that line...)
maligree
It's a point of `C`, definitely not of `C++`. I would bash on the head anyone who would use a `#define` for a constant in `C++`. Macros are best avoided when alternatives exist.
Matthieu M.
This is tagged C++ and yet the text indicates it may be C only. If it's C++ you should use reinterpret_cast for searchability.
Mark B
@Mark, @Matthieu: I've added a C++ version just for your entertainment. :-)
Chris Jester-Young
Why using a function and not constant ? (Just tell me off if I bother you :p)
Matthieu M.
@Matthieu: I don't know how to use a constant and still avoid external linkage (which would make it unusable in a header file).
Chris Jester-Young
Chris, I get the impression that your C++ solution is only for reading, not writing ... am I correct in that matter?
maligree
Chris Jester-Young
@Chris: Okaay ... that would be another finer point of C++. I would never had guessed that there could be some cases where you could use a function-type-looking lvalue as part of an assignment ...
maligree
@maligree: In so many ways, C++ is so very different from C.... :-P (BTW, this is why other commenters got on your case about referring to "C/C++"; C and C++ are such different languages that you should refer to them separately.)
Chris Jester-Young
@Chris: Yeah, but in the end ... I'm actually interested in both solutions (one for C, one for C++), as I'm not skilled in either one. Actually, before asking here, I was thinking about doing the whole stuff with asm, since I know what to do in asm and that part of the project could never become platform-independent. But I wanted it to be beautiful code ... so I'm here :-)
maligree
+1  A: 
#define UART0  ((volatile unsigned int*)0x4000C000)
#define UART0CTL (UART0 + 0x0C)
Why `+ 10`? ___
KennyTM
@dkrueger: 0x0C only works for 32-bit pointers. Granted, this is likely to be the case for ARM, but still, highly unportable. :-P
Chris Jester-Young
@Chris: Not like that matters, the whole thing is highly unportable to begin with. (However, `0x30 / sizeof(int)` would be more clear, IMHO.)
Roger Pate
@KennyTM The + 10 was due to my initial misreading of the offset.@Chris On the platform he specified, unsigned ints are 32-bits.@Roger I think leaving out the offset would make things the clearest: ((volatile unsigned int *)0x4000C030), but I was trying to mimic the original form of his code.
+1: I use this solution all the time on different embedded projects.
S.C. Madsen
+10  A: 

I'd like to be a nitpick: are we talking C or C++ ?

If C, I defer to Chris' answer willingly (and I'd like the C++ tag to be removed).

If C++, I advise against the use of those nasty C-Casts and #define altogether.

The idiomatic C++ way is to use a global variable:

volatile unsigned int& UART0 = *((volatile unsigned int*)0x4000C000);
volatile unsigned int& UART0CTL = *(&UART0 + 0x0C);

I declare a typed global variable, which will obey scope rules (unlike macros).

It can be used easily (no need to use *()) and is thus even shorter!

UART0CTL &= ~1; // no need to dereference, it's already a reference

If you want it to be pointer, then it would be:

volatile unsigned int* const UART0 = 0x4000C000; // Note the const to prevent rebinding

But what is the point of using a const pointer that cannot be null ? This is semantically why references were created for.

Matthieu M.
because you can put a const pointer in a header file and #include it from both C and C++.Might as well declare it "static" as well in C (this is also harmless in C++) as there's no need for it to end up in the symbol table.
Ben Voigt
True, I only thought of the `C++` aspect of the question since `#define` are traditional in `C` programs.
Matthieu M.
Well, this seems to me like it's the best answer for my project as I don't have reason to disbelieve the claim about the "idiomatic C++ way"...
maligree
A: 

You can go one further than Chris's answer if you want to make the hardware registers look like plain old variables:

#define UART0     0x4000C000
#define UART0CTL (*((volatile unsigned int *) (UART0 + 0x30)))

UART0CTL &= ~1;

It's a matter of taste which might be preferable. I've worked in situations where the team wanted the registers to look like variables, and I've worked on code where the added dereference was considered 'hiding too much' so the macro for a register would be left as a pointer that had to be dereferenced explicitly (as in Chris' answer).

Michael Burr
+1  A: 

I like to specify the actual control bits in a struct, then assign that to the control address. Something like:

typedef struct uart_ctl_t {
    unsigned other_bits : 31;
    unsigned disable : 1;
};
uart_ctl_t *uart_ctl = 0x4000C030;
uart_ctl->disable = 1;

(Apologies if the syntax isn't quite right, I haven't actually coded in C for quite awhile...)

TMN
+1  A: 

Another option which I kinda like for embedded applications is to use the linker to define sections for your hardward devices and map your variable to those sections. This has the advantage that if you are targeting multiple devices, even from the same vendor such as TI, you will typically have to alter the linker files on a device by device basis. i.e. Different devices in the same family have different amounts of internal direct mapped memory, and board to board you might have different amounts of ram as well and hardware at different locations. Here's an example from the GCC documentation:

Normally, the compiler places the objects it generates in sections like data and bss. Sometimes, however, you need additional sections, or you need certain particular variables to appear in special sections, for example to map to special hardware. The section attribute specifies that a variable (or function) lives in a particular section. For example, this small program uses several specific section names:

      struct duart a __attribute__ ((section ("DUART_A"))) = { 0 };
      struct duart b __attribute__ ((section ("DUART_B"))) = { 0 };
      char stack[10000] __attribute__ ((section ("STACK"))) = { 0 };
      int init_data __attribute__ ((section ("INITDATA")));

      main()
      {
        /* Initialize stack pointer */
        init_sp (stack + sizeof (stack));

        /* Initialize initialized data */
        memcpy (&init_data, &data, &edata - &data);

        /* Turn on the serial ports */
        init_duart (&a);
        init_duart (&b);
      }

Use the section attribute with global variables and not local variables, as shown in the example.

You may use the section attribute with initialized or uninitialized global variables but the linker requires each object be defined once, with the exception that uninitialized variables tentatively go in the common (or bss) section and can be multiply “defined”. Using the section attribute will change what section the variable goes into and may cause the linker to issue an error if an uninitialized variable has multiple definitions. You can force a variable to be initialized with the -fno-common flag or the nocommon attribute.

NoMoreZealots