0

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?
1

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.

  • 1
    Well thanks, but that's a workaround. I actually did something similar, but limited to the asm directives, so that I could at least use my reference aliases in the C++ code. I guess this fine docrine point about the difference between a reference and a constant pointer is just another small hole in the atrociously convoluted specs of C++ the poor guys who implemented the inline assembler fell into :D. – kuroi neko Aug 10 '17 at 20:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.