[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 |
Hello,
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
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
reti
------------------------
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
GetCounter:
ldi r30, lo8(counter)
ldi r31, hi8(counter)
ld r22, Z+
ld r23, Z+
ld r24, Z+
ld r25, Z+
ret
------------------------
<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>
------------------------
#include
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.
Omar
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
- Re: [avr-gcc-list] How to reserve registers,
Omar Choudary <=
Re: [avr-gcc-list] How to reserve registers, Volker Kuhlmann, 2012/01/03