user37001: 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& tRegister;

constexpr volatile uint8_t& timer_register (volatile uint8_t& a, volatile uint8_t& 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"(&OCRA));

will get an error: impossible constraint in 'asm'
Now this:
asm volatile("lds r24, %0" : : "i"(&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?


user7508: 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(&OCR0A, &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.

37001: <p>I want to write a driver that can use either timer0 or timer2, based on a choice made at compile time.</p>

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

<p>I could do it with plain macros, like so:</p>

<pre><code>#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)
</code></pre>

<p><strong>but</strong> for some reason I don't want to use macros there.</p>

<p>So I came up with this scheme:</p>

<pre><code>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);
</code></pre>

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

<pre><code>asm volatile("lds r24, %0" : : "i"(&amp;OCRA));
</code></pre>

<p>will get an <code>error: impossible constraint in 'asm'</code></p>

<p>Now this:</p>

<pre><code>asm volatile("lds r24, %0" : : "i"(&amp;timer_register (OCR0A , OCR2A )));
</code></pre>

<p>will compile (and produce <code>lds r24, 0x00B3</code> as it should).</p>

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

<p>So my questions are:</p>

<ul>
<li>what is going on there?</li>
<li>how to alias these registers (without macros) to please the inline assembler?</li>
</ul>
 7508: <p>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 <em>pointer to
constant</em>), I do not know how to declare a “reference with constant
address” (which would be the equivalent of a <em>constant pointer</em>). And
this is your issue: the <code>"i"</code> constraint in the <code>asm</code> statement requires
a constant.</p>

<p>Here is my take with pointers:</p>

<pre><code>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));
</code></pre>

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

<pre><code>#define OCR0A (*address_of_OCR0A)
</code></pre>

<p>If you want this convenience, you may have to resort to using similar
macros yourself.</p>
