Is Heisenburg inside my app?

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

Hi Freaks,

Currently I'm wrestling with a rather bizarre problem in my project. Part of the code involves a timeout, which is made from a timer running of a watch crystal at a compare rate of 1Hz. Each second, the AVR wakes up and executes the compare ISR, where a global is incremented. In the main code a function is called which - if the elapsed seconds is more than or equal to a timeout value - sleeps the AVR.

The code is as follows:

ISR(TIMER2_COMP_vect, ISR_BLOCK)
{
	// Autosleep Timeout: 1Hz

	SleepTimeOutSecs++;
}

The globals are declared as:

volatile uint8_t  SleepTimeOutSecs    = 0;
volatile uint8_t  SecsBeforeAutoSleep = 0;

And the sleep routine is:

void MAIN_MenuSleep(void)
{
	if (SecsBeforeAutoSleep && (SleepTimeOutSecs >= SecsBeforeAutoSleep))
	  MAIN_SleepMode();
	else
	  SLEEPCPU(SLEEP_POWERSAVE);
}

Now, the problem is that the expected delay is MUCH longer than what I'm getting. As a test, I added in code to beep quickly each time the ISR fires. I got a nice steady beep at once a second - but what's more the code now works!

I then changed the beep to a LCD update; each time the ISR fires the current value of SleepTimeOutSecs would be displayed. Again, it all works perfectly, with the timeout duration being correct!

To check my sanity, I hooked up my Dragon and set a breakpoint on the ISR. Works perfectly, with no code changes. Turn off the breakpoint, and it breaks.

Now I'm stuck with a dilemma. I can fix the problem by adding in a millisecond delay to the top of the ISR - but why is this necessary? Why should the update be corrupted if the AVR wakes from power down mode to increment a single byte volatile global, unless a delay or breakpoint is set?

Any suggestions or ideas welcome.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

Last Edited: Sun. Apr 15, 2007 - 11:41 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dean,

Try adding some extra code immediately after "SleepTimeOutSecs++;" Then place your breakpoint there. Run the Dragon and at the breakpoint examine your variable, SleepTimeOutSecs. I think you will find it is not incrementing, or incrementing only some of the time.
I don't know why this is occurring, but I have heard of the issue in other micros before. PICs had a big problem with this. The first instruction after a SLEEP would get discarded by the PIC.
If you verify that the AVR is having problems with the increment with a post_inc breakpoint, then try adding a NOP or two at the start of the ISR.
If the AVR increments fine with a post_inc breakpoint, then it would seem that the issue may be the AVR going back to SLEEP before the increment is finished. Try adding a NOP or two after then increment in that case.

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

No cigar. Testing shows that it works great with a 150us-1ms delay either before or after the increment, but fails with anything much shorter. This is really odd!

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

What's the disassembly of your ISR(TIMER2_COMP_vect, ISR_BLOCK) look like?

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

Disassembly (without delay):

/*
 NAME:      | TIMER2_COMP_vect (ISR, non-blocking)
 PURPOSE:   | ISR to manage a timeout for automatically sleeping after periods of user inactivity
 ARGUMENTS: | None
 RETURNS:   | None
*/
ISR(TIMER2_COMP_vect, ISR_BLOCK)
{
    2a7a:	1f 92       	push	r1
    2a7c:	0f 92       	push	r0
    2a7e:	0f b6       	in	r0, 0x3f	; 63
    2a80:	0f 92       	push	r0
    2a82:	11 24       	eor	r1, r1
    2a84:	8f 93       	push	r24
	// Autosleep Timeout: 1Hz

	SleepTimeOutSecs++;
    2a86:	80 91 79 01 	lds	r24, 0x0179
    2a8a:	8f 5f       	subi	r24, 0xFF	; 255
    2a8c:	80 93 79 01 	sts	0x0179, r24
    2a90:	8f 91       	pop	r24
    2a92:	0f 90       	pop	r0
    2a94:	0f be       	out	0x3f, r0	; 63
    2a96:	0f 90       	pop	r0
    2a98:	1f 90       	pop	r1
    2a9a:	18 95       	reti
}

Pretty straightforward. The delay is inserted before or after the increment.

I'm fairly certain this is not a silly software bug, as it is a simple operation on a volatile global single byte register, and the behavior changes if I add or remove breakpoints (or delays). Very strange.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Hum. What clock source are you using? What start-up delay fuse bits have you chosen?

Maybe the clock hasn't had a chance to fully stabilize following wake-up before the CPU starts trying to run code -- the datasheet tells us that too much variation in the CPU clock period from one cycle to the next can lead to unreliable operation.

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

Internal RC oscillator, calibrated via the on-board 32.768KHz watch crystal down to 7.372800MHz.

Nothing else seems to suffer from any odd problems, just this variable increment in this ISR.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

How fast is your clock? 150uS-1mS seems like a lot of cycles!
If the increment is occurring properly with the breakpoint on either side of the increment, it seems like this increment is not completing before you go back to SLEEP. The breakpoint suspends the return to SLEEP and everything works.

Did you try copying the SleepTimeOutSecs to a local variable, incrementing the local and then copying this back to SleepTimeOutSecs? If you code it that way as a test and then do the copy back multiple times, it may give you a clue.

What is the targetted part? Perhaps there is an Atmel Errata?

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

Butterfly, running a MEGA169 (not sure of the revision). Nothing obvious in the errata about this.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

I just remembered a comment I put in my own code two years ago (CAN128):

//we need to wait a couple of 32kHz ticks for the irq to update properly,
//and work around that undocumented bug in T2ASY.
//At 16MHz, 3 osc1 cycles is 977 xtal clocks. It doesn't matter if we
//run at 8MHz, the time is entirely dominated by the xtal startup.

As I remember it, the avr wakes up on the overflow, but, the interrupt happens 1 or 2 tosc2 clock (in your case 31 usec) later than that, so your code wakes up, and then goes to sleep before the irq executes.

This is a side effect of the synchronization circuit in timer 2 in async mode; Since the master clock isn't running, the synchronization registers aren't being clocked, so the designer's only option is to wake up on the raw overflow signal. Then, in order to be certain of proper setup and hold time in the interrupt logic, the interrupt is taken post-synchronization. And due to the way it's implemented, that only updates on the next tosc2 edge plus a few (6?) cpu clocks.
This is related to one of my posts in the undocumented errata thread.

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

Is it possible that some other part of the code is run to set up the delay function you use in the ISR and that section changes the clock speed? So when you use the delay you are using a 'normal' clock, but when you aren't using it you are using some special sleep mode clock - all set up for you elsewhere? Could the complier optimization be doing something depending on the functions you are using? I'm thinking that optimization when using sleep modes may mess with the clock.

How are you controlling your gamma ray input? Does the system have a slight odor of cyanide (almonds) ? Are you sure the cat is still alive?

Smiley

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

I am easily confused, if the uC goes back to SLEEP before the ISR executes, then would the breakpoint ever execute?

My understanding is that Dean is getting into the ISR, right?

Are you able to verify this quirk on multiple Butterflys? Is it possible that this one $20 board is bad?

Please don't flame me, but from personal experience, you did change the batteries and or verified the power source? Butterflys are kind of known for misbehavior on wonky power!

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

If there is a breakpoint set, then the avr won't stop the oscillator; the oscillator is kept running when in powersave.

In my setup, what happened was something like this:
t=0: timer 2 overflows, and the avr wakes up
t=2 ms: the oscillator is stabilized, starts providing clocking.
t=2ms + a few clocks: we go to sleep again.
t=2ms + 31 usec: the interrupt flag on the t2async side gets set. We are sleeping.
t=8 seconds: timer 2 overflows and wakes up the cpu. The interrupt from the previous overflow (@2ms) fires, and in the process clears not only the @2ms overflow flag but, also clears the overflow flag in the timer
t=16 seconds: we get woken up, and go to sleep again

and so the clock advances at half rate. It's a curious thing.

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

That sounds pretty similar to what I'm seeing, KKP. I suppose the other debug code I tried was providing a long enough delay to prevent the AVR from sleeping before the ISR had finished. The breakpoint keeping the main clock alive would also account for the fact that it all works fine when I set one in the ISR.

Quote:

Is it possible that some other part of the code is run to set up the delay function you use in the ISR and that section changes the clock speed?

Nope. Startup calibrates the OSCCAL register, and then it's left alone for the most part.

Quote:

Are you able to verify this quirk on multiple Butterflys? Is it possible that this one $20 board is bad?

It's on a brand-new battery. I can test on another board later, but I think it would be the same result.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

But what I'm wondering is, does the library delay routine do anything to the clock to assure an accurate 1 ms delay? I mean, how does it know what your clock speed is and how does it adjust itself to that clock speed when it is a library routine? Funny I've never even considered this before, though now that I think about it I can speculate on several techniques... I guess I'll go look at the source code and see how it handles that.

Smiley

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

> But what I'm wondering is, does the library delay routine do
> anything to the clock to assure an accurate 1 ms delay?

No, it wouldn't even know how to do this for a particular CPU type.

> I mean, how does it know what your clock speed is and how does it
> adjust itself to that clock speed when it is a library routine?

It's not a library `routine' as such, it is inline code that solely
relies on whatever you pass it as F_CPU.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Okay, now I remember where this weird idea is coming from, it isn't the avr-libc delay functions but the Butterfly delay function:

void Delay(unsigned int millisec)
{
    // mt, int i did not work in the simulator:  int i; 
	uint8_t i;
    
    while (millisec--)
        	//mt: for (i=0; i<125; i++);
			for (i=0; i<125; i++)  
				asm volatile ("nop"::);
}

This clearly relies on a specific CPU clock to be accurate. The reason this thought kept bubbling up was that when I first started using the Butterfly I noticed that this Delay didn't seem to always create the same delay as I judged by blinking LEDs. It turned out that the UART calibration changed the system clock from what the initial OSCAL calibration had set it.

When you said you could get it working using a 1 millisec delay in the ISR, it rang a bell about inaccurate delays in the Butterfly.

And, I just looked at the avrlibc delay functions and I don't see how they get passed the F_CPU, but I'll do some more reading and if I can't figure that out, I'll ask on the GCC forum. [edit]I've been using an older version of WinAVR that doesn't have the delay functions that use the FCPU based delays. I just read the source and see how they work. Thanks Jörg.[/edit]

Sorry for the confusion.

Smiley

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

Dean,

Did you ever get this solved? Is it similar to what KKP reported?

Thanks.

-- Steve

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

It's solved, but only via the bandaid approach of putting in a 1ms delay to the ISR along with a comment to myself for the future. I'm certain it's a bug in the chip, but I've been unable to test that on another butterfly as-of-yet to truly confirm it.

Darn strange behavior however! I'd agree with KKP's analysis - or at least to something closely related. It's good to confirm I'm not going crazy - I was a bit hesitant to post this just in case it was proved I'd made a silly, basic, moronic mistake.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Tested on first-generation butterfly, internal RC oscillator, scope on two IO pins, and the divider set so it wakes up on overflow. The main loop is "set pin A high, wait 1ms, set pin A low, sleep." The interrupt is "toggle pin B".

Result: Pin A goes high, 31 usec later B toggles.

Dean, there are two kind of mistakes: Beginner mistakes, and advanced mistakes. The first ones are nice, most everyone can help you. I'm afraid you've progressed to making advanced mistakes; Now problem descriptions will only get longer, and the number of people able to help will seem to get cut in half every time you need it.

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

Thanks KKP, so the problem's verified then. Nice to be vindicated of making a dumb mistake! It should be noted that I do make silly errors quite frequently (as do, I hope, we all) but I'm capable of solving them myself now. Thanks for the help.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!