Binding a variable to a register

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using the Atmel AVR toolchain3.4.3.12 to compile test programs for the AT tiny2313A device.

The avr-libc-user-manual.pdf supplied with this toolchain there is an FAQ section (section 11 page 63 a question is raised: How to permanently bind a variable to a register?

The code that is given in the faq answer is:

register unsigned char counter asm("r3");

but writing this into my program gives a fatal error (something like 'asm function not declared in this scope') - the compiler sees the 'asm' word and a potential function declaration.

What is the correct syntax to bind a variable to a register? Anyone know?

In the above example a single byte (char) variable is being assigned to a register. In my program I have an unsigned long variable which is being incremented by a timer interupt. Is it possible to assign 4 specific registers to hold this variable?

Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

but writing this into my program gives a fatal error (something like 'asm function not declared in this scope') - the compiler sees the 'asm' word and a potential function declaration.

You can only do this to globals:

register uint8_t myR1 asm("r1");

__attribute__((OS_main)) int main(void) {
    while(1) {
        myR1 = 0;
    }
}

generates:

    while(1) {
        myR1 = 0;
   4:	11 24       	eor	r1, r1
    }
   6:	fe cf       	rjmp	.-4      	; 0x4 <__zero_reg__+0x3>

However you do realise that you will probably not be able to call library functions if you do this as they may have been built to use your "reserved" register?

Oh and this:

register uint32_t myregs asm("r6");

__attribute__((OS_main)) int main(void) {
    while(1) {
        myregs++;
    }
}

results in this:

    while(1) {
        myregs++;
   4:	d4 01       	movw	r26, r8
   6:	c3 01       	movw	r24, r6
   8:	01 96       	adiw	r24, 0x01	; 1
   a:	a1 1d       	adc	r26, r1
   c:	b1 1d       	adc	r27, r1
   e:	3c 01       	movw	r6, r24
  10:	4d 01       	movw	r8, r26
    }
  12:	f8 cf       	rjmp	.-16     	; 0x4 <__zero_reg__+0x3>

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No, you can also have local register variable, they just work a bit different. Just have a look at the GCC documentation.

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

SprinterSB wrote:
No, you can also have local register variable, they just work a bit different. Just have a look at the GCC documentation.
Indeed.

Curiously, and in contradiction to the docs, a local register variable can be declared without asm("rN"):

#include 
int __attribute__ ((__OS_main__)) main (void) {
  register uint8_t foo;
  while(1) {
    foo++;
  }
}
00000080 
: 80: cd b7 in r28, 0x3d ; 61 82: de b7 in r29, 0x3e ; 62 84: 81 2f mov r24, r17 86: 11 e0 ldi r17, 0x01 ; 1 88: 18 0f add r17, r24 8a: fc cf rjmp .-8 ; 0x84

However, this only seems to work for single-byte (single-register) variables:

  register uint16_t foo;
00000080 
: 80: cd b7 in r28, 0x3d ; 61 82: de b7 in r29, 0x3e ; 62 84: ff cf rjmp .-2 ; 0x84

I doubt this is a 'feature'.

---------
EDIT: Ah, I see.
---------

Also omitted from the docs is the requirement that a multi-byte register variable be assigned to an appropriately aligned register:

  register uint16_t foo asm("r3");
foo.c: In function 'main':
foo.c:5:21: error: register specified for 'foo' isn't suitable for data type
   register uint16_t foo asm("r3");
                     ^

The error isn't especially revealing.

In the case of a two-byte variable aligning to an even register fixes it:

  register uint16_t foo asm("r4");
00000080 
: 80: cd b7 in r28, 0x3d ; 61 82: de b7 in r29, 0x3e ; 62 84: c2 01 movw r24, r4 86: 01 96 adiw r24, 0x01 ; 1 88: 2c 01 movw r4, r24 8a: fc cf rjmp .-8 ; 0x84

The word-aligned requirement also applies to wider types (e.g. __uint24, uint32_t, uint64_t). Alignment need not agree with the size of those types (e.g. 3, 4, 8 bytes).

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
However you do realise that you will probably not be able to call library functions if you do this as they may have been built to use your "reserved" register?
My understanding is that so long as you stick to registers in the call-saved list (r2-r17, r28-r29) there would be no conflict with library code that complies with the ABI.

The obvious caveat is that if you are declaring a volatile global register variable for use in an ISR, you would need to ensure atomic execution of a library call which might save/use/restore that(those) register(s).

This might disable interrupts for an extended period, a potentially serious problem esp. since presumably the register variable was desired for performance reasons in the first place.

Since the OP intends to use this in a timer interrupt, there remains the issue that volatile global register variables are not actually supported properly.

With the latest toolchain (3.4.3 based on 4.8.1):

register.c:3:1: warning: optimization may eliminate reads and/or writes to register variables [-Wvolatile-register-var]
 volatile register uint64_t foo asm("r2");
 ^

However the links mentioned in this post suggest that it nevertheless might sometimes do the right thing.

Earlier versions (I've tested 4.3.2) don't raise the warning. It's anyone's guess what actually happens under the hood in this case, and whether or not 'the right thing' would be done in all circumstances.

I like skeeve's take:

Quote:
If one prefers, one can call it an occasionally inconvenient (and hard to squash) non-orthogonality that is documented.
:)

If this register variable will be private to the timer interrupt and used for other purposes, obviously volatile isn't needed and this issue disappears.

In any event, use of global register variable will almost certainly cause additional register pressure throughout the rest of all but the most trivial of apps.

How would -ffixed= affect the compiler's use of a register? Could this be used as an alternative to the elusive volatile register variable?

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for all the replies. Very helpful information here.

I'll need to spend some time exploring these methods - my experience of C and C compilers is very limited.

I actually have more knowledge and experience with assembly language programming (8031/8051 devices) and more recently with the Arduino Uno units and the Arduino IDE.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just out of interest WHY do you think you even need to bind a variable to a register anyway?

C normally does a pretty good job with register allocation anyway.