18

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

orithena
  • 1,455
  • 1
  • 10
  • 24
  • 4
    `#define UART0CT` to `#define X`? – Pratik Deoghare Mar 10 '10 at 13:34
  • @The Machine Charmer: +1, funny. I don't think the OP meant shorter in a code-golfing fashion, but, nice. :-) – C. K. Young Mar 10 '10 at 13:39
  • Standard C *requires* an explicit cast in ordetr to assign a non-zero integer to pointer. Your code, in its current form, is only valid in GCC, but broken from pedantic C point of view. So, whether you want it or not, you'll have to make it longer if you really want to keep it as valid C. Of course, if you want a GCC-specific solution, it is a different story. – AnT stands with Russia Mar 10 '10 at 19:04
  • @PratikDeoghare that... that was _hilarious_ haha! – salezica Jul 19 '12 at 20:51

6 Answers6

21
#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.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • @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) – C. K. Young Mar 10 '10 at 13:45
  • 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...) – orithena Mar 10 '10 at 14:16
  • 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. Mar 10 '10 at 14:23
  • 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 Mar 10 '10 at 14:30
  • @Mark, @Matthieu: I've added a C++ version just for your entertainment. :-) – C. K. Young Mar 10 '10 at 16:49
  • Why using a function and not constant ? (Just tell me off if I bother you :p) – Matthieu M. Mar 10 '10 at 17:09
  • @Matthieu: I don't know how to use a constant and still avoid external linkage (which would make it unusable in a header file). – C. K. Young Mar 10 '10 at 17:40
  • Chris, I get the impression that your C++ solution is only for reading, not writing ... am I correct in that matter? – orithena Mar 10 '10 at 19:30
  • @maligree: It can write too (returns a non-const reference). So you could say `uart0ctl() = 42` if you want. The key ingredient is the `&` in `unsigned volatile&`. :-) – C. K. Young Mar 10 '10 at 19:47
  • @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 ... – orithena Mar 11 '10 at 11:40
  • @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.) – C. K. Young Mar 11 '10 at 14:01
  • @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 :-) – orithena Mar 11 '10 at 16:43
18

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.
  • 287,565
  • 48
  • 449
  • 722
  • 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 Mar 10 '10 at 14:29
  • True, I only thought of the `C++` aspect of the question since `#define` are traditional in `C` programs. – Matthieu M. Mar 10 '10 at 14:34
  • 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"... – orithena Mar 10 '10 at 19:22
2

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
  • 333,147
  • 50
  • 533
  • 760
1
#define UART0  ((volatile unsigned int*)0x4000C000)
#define UART0CTL (UART0 + 0x0C)
user261840
  • 168
  • 2
  • @dkrueger: 0x0C only works for 32-bit pointers. Granted, this is likely to be the case for ARM, but still, highly unportable. :-P – C. K. Young Mar 10 '10 at 13:44
  • @Chris: Not like that matters, the whole thing is highly unportable to begin with. (However, `0x30 / sizeof(int)` would be more clear, IMHO.) –  Mar 10 '10 at 14:15
  • @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. – user261840 Mar 10 '10 at 14:41
  • +1: I use this solution all the time on different embedded projects. – S.C. Madsen Mar 10 '10 at 16:54
1

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
  • 3,060
  • 21
  • 23
  • I'm doubtful about whether the order of bits in a bitfield is specified by the standard. I guess, this code would be implementation defined behavior. But the idea of using a `struct` is good: Define a `struct uart` that contains the memory layout of the memory mapped device, assign the address to a single pointer `uart* const uart_regs = 0x4000c000;`, and then access via that object with `uart_regs->ctl &= ~1;`. – cmaster - reinstate monica Feb 14 '18 at 14:35
1

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.

P.P
  • 117,907
  • 20
  • 175
  • 238
NoMoreZealots
  • 5,274
  • 7
  • 39
  • 56