[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [avr-gcc-list] How to reserve registers

From: Omar Choudary
Subject: Re: [avr-gcc-list] How to reserve registers
Date: Tue, 3 Jan 2012 12:50:46 +0000


First of all happy new year and sorry for the huge delay in replying.

Thanks for your comments. In my application I am using external
libraries based on avrlibc
so the approach with ffixed doesn't work, although thanks for the hint.

Also, apparently the "register ..." statement in C doesn't seem to
avoid other libraries (probably
already compiled to use certain registers) in messing up with reserved
registers. Therefore
I had to use another approach like this:

first, I declared a C variable which will point somewhere in RAM and
then a few assembler
registers that will be used to access that variable:

<file counter.h>
 * Global register variables.
#ifdef __ASSEMBLER__

// Syncronization counter, 32 bit value accessed through 4 CONSECUTIVE registers
// In the code, these values should be loaded using the C global
variable as follows
#  define counter_b0    r18
#  define counter_b1    r19
#  define counter_b2    r20
#  define counter_b3    r21

// Variables used in asm subroutines to store temporary values
#  define counter_inc   r22
#  define counter_sreg  r23

#else  /* !ASSEMBLER */

#include <stdint.h>

uint32_t counter; // this will be updated and used in both C and asm code

#endif /* ASSEMBLER */

Then I changed the interrupt routine to do all the processing there, first
retrieving the bytes from RAM, then updating them and then storing back:
<file main.S>
.global TIMER2_COMPA_vect
    push counter_sreg                    ; used for SREG
    in  counter_sreg, _SFR_IO_ADDR(SREG) ; save SREG
    cli                                  ; disable interrupts
    push counter_b0
    push counter_b1
    push counter_b2
    push counter_b3
    push counter_inc
    push r30
    push r31

    ldi r30, lo8(counter)
    ldi r31, hi8(counter)
    ld counter_b0, Z
    ldd counter_b1, Z+1
    ldd counter_b2, Z+2
    ldd counter_b3, Z+3
    ldi counter_inc, 1                   ; use to increment with carry
    add counter_b0, r22
    adc counter_b1, r1
    adc counter_b2, r1
    adc counter_b3, r1
    st Z, counter_b0
    std Z+1, counter_b1
    std Z+2, counter_b2
    std Z+3, counter_b3

    pop r31
    pop r30
    pop counter_inc
    pop counter_b3
    pop counter_b2
    pop counter_b1
    pop counter_b0

    out _SFR_IO_ADDR(SREG), counter_sreg
    pop counter_sreg

You can then declare another assembler routine to get the variable exported
into the right registers for access in C, like this:
<file main.S or other.S>
.global GetCounter
    ldi r30, lo8(counter)
    ldi r31, hi8(counter)
    ld r22, Z+
    ld r23, Z+
    ld r24, Z+
    ld r25, Z+

<file main.h or other.h>
uint32_t GetCounter();

Finally, you can access the updated counter in a C file like this:
<file main.c or other.c>
uint32_t counter_value;
counter_value = GetCounter();

When you compile with avr-gcc include all .c and .S files together.

Or you could of course just use the "counter" value directly, by
importing the external variable
<file main.c or other.c>
#include "counter.h"
extern uint32_t counter;

uint32_t counter_value;
counter_value = counter;

However I prefer the first approach, although it requires extra clock
cycles because
is more portable and cleaner across multiple files (you just export
the header file, rather
than having to use the definition of the external variable).

In any case, thanks to all for the help and I hope this might help others.


On Fri, Dec 16, 2011 at 11:35 PM, Jens Bauer <address@hidden> wrote:
> Hi Omar,
>>> .global IncrementCounter
>>> IncrementCounter:
>>>     ldi r19, 1                          ; use to increment with carry
>>>     clc
>>>     adc counter_b0, r19
>>>     adc counter_b1, r1
>>>     adc counter_b2, r1
>>>     adc counter_b3, r1
>>>     ret
> ... or free r19 (this is an example of a pure interrupt only, which you could 
> place directly in the interrupt vectors):
> TIMER2_COMPA_vect:          ;[5]
>    sec                     ;[1]
>    adc     counter_b0,r1   ;[1]
>    adc     counter_b1,r1   ;[1]
>    adc     counter_b2,r1   ;[1]
>    adc     counter_b3,r1   ;[1]
>    reti                    ;[5]
>                            ;[15]
> Numbers in brackets are clock-cycles spent for the operation.
> (eg. an interrupt takes 5 clock-cycles. If you don't place the code directly 
> in the interrupt-vectors, you'd probably use rjmp to jump to the code; which 
> means you'd need to add 2 extra clock cycles in that case.
> If you need the counter to spend as little CPU-time as possible, you could 
> alternatively do the following, but that means you'd get an unstable 
> CPU-usage:
> TIMER2_COMPA_vect:          ;[5]
>    sec                     ;[1]
>    adc     counter_b0,r1   ;[1]
>    brcs    ad2             ;[2/1]
>    reti                    ;[5]
>                            ;(13)
> ad2:                        ;[9]
>    adc     counter_b1,r1   ;[1]
>    brcs    ad3             ;[2/1]
>    reti                    ;[5]
>                            ;(16)
> ad3:                        ;[12]
>    adc     counter_b2,r1   ;[1]
>    adc     counter_b3,r1   ;[1]
>    reti                    ;[5]
>                            ;(19)
> -As I mentioned above; it would use slightly less CPU-time, but when you get 
> a carry, the CPU-usage would generate a spike, which might not be what you 
> want, so the above routine would spend between 13 and 19 clock cycles.
> Love,
> Jens

reply via email to

[Prev in Thread] Current Thread [Next in Thread]