using references to SFRs in gcc inline assembler I want to write a driver that can use either timer0 or timer2, based on a choice made at compile time.

So I want all references to timer registers to be abstracted, e.g. OCRA for OCR0A/OCR2A, etc.

I could do it with plain macros, like so:

#define _CONCAT3(a,b,c)   a##b##c
#define CONCAT3(a,b)   _CONCAT3(a,b,c)

// set timer number globally
#define MY_TIMER 2

// define an alias for each control register
#define OCRA CONCAT3(OCR, MY_TIMER, A)


but for some reason I don't want to use macros there.

So I came up with this scheme:

typedef volatile uint8_t&amp; tRegister;

constexpr volatile uint8_t&amp; timer_register (volatile uint8_t&amp; a, volatile uint8_t&amp; b)
{ return MY_TIMER==0 ? a : b; }

tRegister OCRA = timer_register (OCR0A, OCR2A);


It works pretty well for all its awkwardness, but when I try to use this definition in an inline assembly directive, I get strange results:

asm volatile("lds r24, %0" : : "i"(&amp;OCRA));


will get an error: impossible constraint in 'asm'

Now this:

asm volatile("lds r24, %0" : : "i"(&amp;timer_register (OCR0A , OCR2A )));


will compile (and produce lds r24, 0x00B3 as it should).

By definition a reference to a static variable is a constant address, known at compile time, so I can't see why it can't be used in this context.

So my questions are:


what is going on there?
how to alias these registers (without macros) to please the inline assembler?
You could use pointers instead of references. With pointers, you have
the separate notions of “constant pointer” and “pointer to constant”. A
reference is a kind of pointer coated with syntactic sugar. However,
whereas you can have a “constant reference” (equivalent to pointer to
constant), I do not know how to declare a “reference with constant
address” (which would be the equivalent of a constant pointer). And
this is your issue: the "i" constraint in the asm statement requires
a constant.

Here is my take with pointers:

typedef volatile uint8_t * tRegister;

constexpr tRegister timer_register(tRegister a, tRegister b)
{ return MY_TIMER==0 ? a : b; }

// The `const' below is essential.
const tRegister OCRA = timer_register(&amp;OCR0A, &amp;OCR2A);

asm volatile("lds r24, %0" : : "i"(OCRA));


Of course, you then have to change all your OCRA = ...; statements
with *OCRA = ...;, which is awkward. But note that the avr-libc
defines the SFRs with macros equivalent to

#define OCR0A (*address_of_OCR0A)


If you want this convenience, you may have to resort to using similar
macros yourself.