Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
brandb
PostPosted: Apr 26, 2012 - 04:52 AM
Newbie


Joined: Apr 26, 2012
Posts: 6


On an ATTiny 2313...

I'm trying to get a routine to run for a few seconds, then go to sleep and wait for a pin-change interrupt. It works under some conditions (see the end of this message), but not in the simplest case.

The problem seems to be that the variable I'm using as a flag to tell my main loop that an interrupt timer has gone off is not being checked correctly. What am I doing wrong?

The relevant C, followed by the assembler is as follows...

Interrupt flag and routine in C:

Code:
register uint8_t tick_flag asm("r2");

ISR (TIMER0_OVF_vect) {
  tick_flag = 1;
}


and in assembler:

Code:
ISR (TIMER0_OVF_vect) {
  <prolog>
  tick_flag = 1;
  3e:   22 24           eor     r2, r2
  40:   23 94           inc     r2
}
  <epilog>


So far, so good; r2 is being zeroed and incremented, but when I try to check the flag, I get...


Code:
  for (;;)  // main loop                                                       
    {
      if (tick_flag) { // Problem?                                             
        tick_flag = 0;
        time_to_stay_awake--;


(when time_to_stay_awake counts down to 0, we sleep).

... and in assembler:

Code:
      if (tick_flag) { // Problem?
  80:   99 23           and     r25, r25
  82:   e9 f3           breq    .-6             ; 0x7e <main+0x30>
        tick_flag = 0;
        time_to_stay_awake--;
  84:   81 50           subi    r24, 0x01       ; 1


Not only is is the code to clear r25 never seen, r25 is only set to r2 before the for loop, not in it, so we're not checking the flag that the interrupt routine sets.

In fact, the only way I can get the for-loop to appear at all is to do something with another variable inside of the loop, but outside of the conditional. If it's just the conditional, avr-gcc 'optimizes' the for-loop out entirely -- as if I've got "if (0)..." in there.

By the way, this problem occurs if I'm using vanilla variables as well; the code references two different places in SRAM.

The only way this thing actually works is when there are calls to the eeprom library to write persistent configuration data. I'm guessing that's because there's enough other code and library calls that the registers are all used elsewhere and the variable has to be reloaded, but that's just a wild guess.

The eeprom write case also works with a register declaration, for some reason.

So, can anyone set my cluefulness bit?

Thanks.

Take care,
brad
 
 View user's profile Send private message  
Reply with quote Back to top
mtaschl
PostPosted: Apr 26, 2012 - 05:17 AM
Resident


Joined: Aug 21, 2002
Posts: 895
Location: Austria

You have to declare tick_flag as volatile.
http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_volatile

_________________
/Martin.
 
 View user's profile Send private message  
Reply with quote Back to top
brandb
PostPosted: Apr 26, 2012 - 08:20 AM
Newbie


Joined: Apr 26, 2012
Posts: 6


Thank you *very* much, Martin.

To misquote Eric Raymond, "Given enough bugs, all eyeballs are shallow."

I was paging through the avr-lib documentation forever, but it never occurred to me to look at the FAQ section.

Thanks again.

Take care,
brad
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Apr 26, 2012 - 11:17 AM
10k+ Postman


Joined: Jul 18, 2005
Posts: 62352
Location: (using avr-gcc in) Finchingfield, Essex, England

For more detail read my tutorial:

Optimization and the importance of volatile in GCC

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
innocent_bystander
PostPosted: Apr 26, 2012 - 01:36 PM
Wannabe


Joined: Jun 27, 2008
Posts: 54


mtaschl wrote:
You have to declare tick_flag as volatile.
As far as we remember, in gcc volatile has no effect on register variables: http://gcc.gnu.org/ml/gcc-patches/2005-11/msg00657.html

Here is a bug report: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=17336 but although they mark it as "resolved" and last message states that author can't reproduce it in gcc 4.2.2, I saw it still there in WinAVR 20100110 (gcc 4.3.3). And the only one way to tell the compiler re-read register variable in cycles like in TS example is inline-asm:
Code:
for (;;)  // main loop                                                   
     {
       asm volatile("" :"+r"(tick_flag));
       if (tick_flag) { // no problems!                                             
 
 View user's profile Send private message  
Reply with quote Back to top
theusch
PostPosted: Apr 26, 2012 - 02:00 PM
10k+ Postman


Joined: Feb 19, 2001
Posts: 25921
Location: Wisconsin USA

Consider using GPIORn for your flags. It turns out that the Tiny2313 has a wealth of them in lo I/O address space.
 
 View user's profile Send private message  
Reply with quote Back to top
brandb
PostPosted: Apr 26, 2012 - 04:01 PM
Newbie


Joined: Apr 26, 2012
Posts: 6


@clawson: Nice tutorial; thanks for the pointer.

@innocent_bystander: I just checked it in avr-gcc [gcc version 4.3.3 (GCC)] on OS X Snow Leopard, and
Code:
volatile register uint8_t tick_flag asm("r2");

works correctly (r2 is checked and cleared in the main loop).

@theuch: I figured I was going to have to read the whole ATTiny 2313 data sheet some day:
Quote:
...General Purpose I/O Registers. These registers can be used for storing any information, and they are particularly useful for storing global variables and status flags.

Why yes, yes that does sound useful. : - )

I think I'm going to like it here...

Take care,
brad
 
 View user's profile Send private message  
Reply with quote Back to top
SprinterSB
PostPosted: Apr 26, 2012 - 08:54 PM
Posting Freak


Joined: Dec 21, 2006
Posts: 1485
Location: Saar-Lor-Lux

brandb wrote:
Code:
volatile register uint8_t tick_flag asm("r2");
volatile there has no effect and just serves to confuse yourself.

volatile is meaninful with memory locations but not for objects in storage class "register", it's simply silently ignored.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
theusch
PostPosted: Apr 26, 2012 - 09:30 PM
10k+ Postman


Joined: Feb 19, 2001
Posts: 25921
Location: Wisconsin USA

Quote:

volatile there has no effect and just serves to confuse yourself.

volatile is meaninful with memory locations but not for objects in storage class "register", it's simply silently ignored.

I first read your statements and nodded to myself in agreement.

But then I started to think. (always a dangerous situation)

-- Are you saying that in the GCC implementation, it has no effect?
-- Or are you saying that applying modifier volatile to register storage class in C is not meaningful?
-- Would the same comments apply to "volatile register uint8_t tick_flag;"?

"register" is a hint/suggestion to the compiler to make the variable 'fast' according to my draft standard. If it is in fact assigned to a memory location instead of a register, then wouldn't volatile have meaning?
 
 View user's profile Send private message  
Reply with quote Back to top
SprinterSB
PostPosted: Apr 26, 2012 - 10:37 PM
Posting Freak


Joined: Dec 21, 2006
Posts: 1485
Location: Saar-Lor-Lux

theusch wrote:
Quote:
volatile there has no effect and just serves to confuse yourself.

volatile is meaninful with memory locations but not for objects in storage class "register", it's simply silently ignored.

I first read your statements and nodded to myself in agreement.

But then I started to think. (always a dangerous situation)

-- Are you saying that in the GCC implementation, it has no effect?
-- Or are you saying that applying modifier volatile to register storage class in C is not meaningful?
-- Would the same comments apply to "volatile register uint8_t tick_flag;"?
ad 1: I said "here", i.e with a global register variable.
ad 2: It is not C, register asm is a GNU extention
ad 3: That is vanilla C, not a GNU extention. register is used by GCC in the same way as auto.
Quote:
"register" is a hint/suggestion to the compiler to make the variable 'fast' according to my draft standard.
Again, notice that there are 3 flavours of "register in C:
  1. register as from the C standard, same as auto.
  2. global register asm: GNU-C: bind and fix a machine register to a variable
  3. local register asm: GNU-C: weakly bind a machine register to a variable. The binding comes only into live if the register is used as operand to an inline asm. For example, in the following code, usage of R2 may be optimized out:
    Code:
    char foo (void)
    {
        register char q asm ("2");
        q = 1;
        return q;
    }
    whereas in the following example R2 will be used (but only guaranteed at the respective asm place(s):
    Code:
    char foo (void)
    {
        register char q asm ("2");
        q = 1;
        asm (";":"+r"(q));
        return q;
    }
Unfortunately, the GNU-C register is not very well documented/specified, in particular flavour No 3 which ist the most important and most useful.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
ChaunceyGardiner
PostPosted: Apr 26, 2012 - 10:52 PM
Posting Freak


Joined: Mar 09, 2012
Posts: 1452
Location: North Carolina, USA

It's been a while since I cared about low-level stuff like this, but I think volatile actually prevents the compiler from keeping the variable in a register.
 
 View user's profile Send private message  
Reply with quote Back to top
skeeve
PostPosted: Apr 27, 2012 - 12:17 AM
Raving lunatic


Joined: Oct 29, 2006
Posts: 2654


ChaunceyGardiner wrote:
It's been a while since I cared about low-level stuff like this, but I think volatile actually prevents the compiler from keeping the variable in a register.
That is true for an ordinary global variable.
If it's true for a register-asm global variable, that is a bug.
It's an especially nasty bug if no error message is generated.
SprinterSB wrote:
whereas in the following example R2 will be used (but only guaranteed at the respective asm place(s):
Code:
char foo (void)
{
    register char q asm ("2");
    q = 1;
    asm (";":"+r"(q));
    return q;
}
Unfortunately, the GNU-C register is not very well documented/specified, in particular flavour No 3 which ist the most important and most useful.
I don't see the utility here.
What is the advantage over letting the compiler pick the register?

Also, the R in R2 is missing.

_________________
Michael Hennebry
Iluvatar is the better part of Valar.
 
 View user's profile Send private message  
Reply with quote Back to top
SprinterSB
PostPosted: Apr 27, 2012 - 07:47 AM
Posting Freak


Joined: Dec 21, 2006
Posts: 1485
Location: Saar-Lor-Lux

skeeve wrote:
I don't see the utility here. What is the advantage over letting the compiler pick the register?
You can ligtweight interface to non-ABI functions:
Code:
static inline char __attribute__((always_inline))
foo (char a)
{
    register char q asm ("2") = 1;
    register char s asm ("3") = a;
    asm ("%~call bar"
         : "+r" (q)
         : "r" (s)
         : "4", "5");
    return q;
}
where bar is a function r2 = bar (r2,r3) clobbering r4 and r5.
Quote:
Also, the R in R2 is missing.
It's not "missing", it's redundant.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
brandb
PostPosted: Apr 27, 2012 - 07:31 PM
Newbie


Joined: Apr 26, 2012
Posts: 6


Great discussion.

Summary:
    Use General Purpose I/O Registers (GPIORn) for interrupt flags

    If not using GPIORn, make sure to declare your flag 'volatile'

    Don't depend on volatile declarations of register variables when using the GNU register-asm declaration (although it does work in some instances)

Just for completeness (and the reason I'm posting this), I did the comparison of two versions of the simplest case with only one change between them: the volatile type-qualifier.

For the code
Code:
      if (tick_flag) { // Problem?

Case 1:
Code:
register uint8_t tick_flag asm("r2");

yields
Code:
      if (tick_flag) { // Problem?
  80:   99 23           and     r25, r25
  82:   e9 f3           breq    .-6             ; 0x7e <main+0x30>

Case 2:
Code:
volatile register uint8_t tick_flag asm("r2");

yields
Code:
      if (tick_flag) { // Problem?
  7a:   22 20           and     r2, r2
  7c:   e9 f3           breq    .-6             ; 0x78 <main+0x2a>

But on SprinterSB's advice, this is not something I'll count on.

Take care,
brad
 
 View user's profile Send private message  
Reply with quote Back to top
SprinterSB
PostPosted: Apr 27, 2012 - 08:01 PM
Posting Freak


Joined: Dec 21, 2006
Posts: 1485
Location: Saar-Lor-Lux

In your first snippet with R25, you have at least one of these:
  1. tick_flag is actually not a global register variable.
  2. The value held in R2 is known to the compiler and it knows R25 holds the same value.
  3. You are fooled by badly placed annotations.
  4. You found a compiler bug.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
brandb
PostPosted: Apr 27, 2012 - 11:57 PM
Newbie


Joined: Apr 26, 2012
Posts: 6


SprinterSB wrote:
In your first snippet with R25, you have at least one of these:
  1. tick_flag is actually not a global register variable.

I'm pretty sure it is global, as it's declared just after the #include's (before, and outside the scope of, the ISRs and main), and it is in the same place as the global, non-register variable (commented out for this listing) I've been comparing.
Quote:
  • The value held in R2 is known to the compiler and it knows R25 holds the same value.

  • This is true. Before the for-loop, the compiler loads the value of R2 into R25.
    Code:
      76:   92 2d           mov     r25, r2

    It's not obvious from my previous post, but the for-loop starts on line 7e; the branch statement on line 82 kicks back to the start of the loop.

    So, the code never looks at R2 inside the for-loop, and the for-loop never sees the change caused by the ISR. Thus, my initial problem.
    Quote:
  • You are fooled by badly placed annotations.

  • Not impossible, but I have a fair amount of experience reading assembler, and I'd like to think I'm not that easily confused. I've certainly noticed where the annotations were misleading (and duplicated) in other parts of the listing.
    Quote:
  • You found a compiler bug.

  • I cannot imagine that I've found a bug in something as mature and heavily used as avr-gcc with something this simple.


    Thanks again for the commentary.

    Take care,
    brad
     
     View user's profile Send private message  
    Reply with quote Back to top
    SprinterSB
    PostPosted: Apr 28, 2012 - 02:04 PM
    Posting Freak


    Joined: Dec 21, 2006
    Posts: 1485
    Location: Saar-Lor-Lux

    brandb wrote:
    SprinterSB wrote:
    • The value held in R2 is known to the compiler and it knows R25 holds the same value.
    This is true. Before the for-loop, the compiler loads the value of R2 into R25.
    Code:
      76:   92 2d           mov     r25, r2
    It's not obvious from my previous post, but the for-loop starts on line 7e; the branch statement on line 82 kicks back to the start of the loop.
    You never showed the complete context, so we just can guess...
    brandb wrote:
    So, the code never looks at R2 inside the for-loop, and the for-loop never sees the change caused by the ISR.
    Just as I said, global reg vars in GCC cannot be volatile. GCC will emit a warning like
    gcc -Wall wrote:
    warning: optimization may eliminate reads and/or writes to register variables [-Wvolatile-register-var]
    for a line like
    Code:
    register volatile char r asm ("2");
    From the technical viewpoint, there is not even a way to express that a register is volatile, see the field volatil in gcc/rtl.h. Each RTX has some flags. The volatil bitfield has complete different meanings if the RTX is a REG or a MEM. See how that fields is used in the REG_USERVAR_P and MEM_VOLATILE_P macros.

    brandb wrote:
    SprinterSB wrote:
    • You found a compiler bug.
    I cannot imagine that I've found a bug in something as mature and heavily used as avr-gcc with something this simple.
    Just consider the following
    Code:
    char c;

    void foo (char a)
    {
      a >>= 7;
      if (a)
        c = a;
    }
    Compiled with -O, -Os or -O2 for example, all avr-gcc versions up to 4.6.1 will generate wrong code for that 3-liner.

    This bug was in avr-gcc since beginning of time, and reported 2009, i.e. 9 (nine) years after avr-gcc started.

    It may be the case that avr-gcc is will tested.

    However, many bugs or problems are simply not reported out for laziess.
     
     View user's profile Send private message Visit poster's website 
    Reply with quote Back to top
    skeeve
    PostPosted: Apr 28, 2012 - 09:18 PM
    Raving lunatic


    Joined: Oct 29, 2006
    Posts: 2654


    SprinterSB wrote:
    Just as I said, global reg vars in GCC cannot be volatile. GCC will emit a warning like
    gcc -Wall wrote:
    warning: optimization may eliminate reads and/or writes to register variables [-Wvolatile-register-var]
    for a line like
    Code:
    register volatile char r asm ("2");
    From the technical viewpoint, there is not even a way to express that a register is volatile, see the field volatil in gcc/rtl.h.
    A bug that is documented.
    If one prefers, one can call it an occasionally inconvenient (and hard to squash) non-orthogonality that is documented.

    _________________
    Michael Hennebry
    Iluvatar is the better part of Valar.
     
     View user's profile Send private message  
    Reply with quote Back to top
    Display posts from previous:     
    Jump to:  
    All times are GMT + 1 Hour
    Post new topic   Reply to topic
    View previous topic Printable version Log in to check your private messages View next topic
    Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
    Credits