Re-initialization of global/static variables

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

I'm familiar with the C standard and the avr-libc FAQ about automatic initialization of global/static variables ( http://www.nongnu.org/avr-libc/u... ).

However, I have a question about reinitialization of these variables. I'm using the watchdog timer interrupt for a few functions. One of these functions involves resetting the AVR to its boot-up state. I'm using code like

static inline void reset_mcu(void) __attribute__((always_inline));
static void reset_mcu(void) {
  asm volatile("eor r30,r30");
  asm volatile("eor r31,r31");
  asm volatile("ijmp");
}

to restart the MCU.

Looking at the assembly listing, I see that code then jumps to _ctors_end and appears to copy bss data and zero global and static variables. However, I've not complete investigated the assembly code to see if that is truly the case.

At the moment, in my initialize/reinitialize functions I explicity reset all the global static variables to zero. However, I wonder if can count on the re-zeroing of global/static variables when I ijmp to 0x0000 and thus delete my reinitialization statements.

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

Yes, you will have bss cleared and data loaded on every reset (or anytime you come from the reset vector location). That stuff is in section .init4.

Section .init2 will clear r1, setup the stack.

Sections .init0 and .init1 (and others) can be used for your own use if wanted. The reset vector will XJMP(rjmp or jmp) to __init, which is the start of section .init0, but if you don't add anything to .init0 or .init1, you effectively jump to .init2.

If you download the libc source, you can look at gcrt1.S to see the libc startup (section .vectors, empty .init0, .init2, (.init4 for >64k)). Section .init4 for <=64k comes from libgcc.a I guess, but have not looked at that yet.

here's some version of libgcc.S-
http://gcc.gnu.org/cgi-bin/cvswe...
(not sure where __ctors_end is coming from, its not it grct1.S or libgcc.S, but referred to in libgcc.S)

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

curtvm wrote:
Yes, you will have bss cleared and data loaded on every reset (or anytime you come from the reset vector location). That stuff is in section .init4.
Thanks for the detailed explanation and the source code link. This is very good news as it regains 124 bytes of my MCU's program space eliminating the resetting of global and static variables back to zero after the rjmp to the reset vector.

Thanks again for the very useful reply.

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

Maybe this is what you wanted, if so, disregard. That ijmp to 0 will leave all i/o registers untouched. If you want a 'true' reset, you will have to cause a watchdog timeout (system reset mode).

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

Thanks for the additional information. Yes, I'm aware of that and am reseting the registers in my initialization routines.

In fact, I'm taking an advantage of the i/o registers staying the same to pass information to main() about the reason for the reset (such as watchdog timeout, calibration timeout, user reset) which then modifies the effect main() will have after the reinitialization of the registers.

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

Here's some more info-
I found ctors/dtors stuff in the linker scripts (avrX.x in ldscripts folder).

It looks like that stuff goes right after .vectors, then .progmem data, then section .init0.

The reset vector should point to the start of section .init0 (__init label in gcrt1.S), but will end up at .init2 if nothing added to .init0 and .init1. That __ctors_end label you are getting to, I would assume is just an empty label that shows up, and will be the same as .init2 when nothing is used between .vectors and .init2.

I'm not sure what sits in the 'ctors' location, but I have an lss file I'm looking at that has some data there (just before progmem data), and looks unrelated to anything, also no __do_global_ctors/.init6, and am using c not c++, so am confused).

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

curtvm wrote:
I'm not sure what sits in the 'ctors' location, but I have an lss file I'm looking at that has some data there (just before progmem data), and looks unrelated to anything, also no __do_global_ctors/.init6, and am using c not c++, so am confused).

Haven't checked but isn't that where the .data initialisers are located?

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

Take care if you've got a (very) slow clockspeed and have the watchdog timer enabled.

Following a watchdog reset, you've got 15ms to clear the watchdog before it resets itself again. (15ms is the default value that is used following a reset, regardless of the value before the reset)

Putting 'clear the watchdog timer' as the first code in main() might not be good enough if you've got a fair number of global variables being initialised in .init4.

I hit this problem on a Tiny13 running on a 128KHz clock with the DIV8 fuse set. That left me 256 clock cycles to clear the watchdog timer. Because I had a number of global const arrays (for running state-machines), those precious 256 clock cycles ran-out in the middle of the .init4 code. That resulted in the device being continually reset.

My solution was to write a small (pseudo) function to clear the watchdog timer and use an attribute to place it in .init3. This code is (obviously?) run before .init4, hence well within the 256 cycle deadline.

I'm impressed that I've got this sort of ability without having to resort to assembler.

(In hindsight, I could have put those const arrays into PROGMEM - it now seems wrong to be holding const values in SRAM)

Nigel.

Nigel Batten
www.batsocks.co.uk

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

I was tricked. That '00000068 <__ctors_end>:' label got me screwed up (even though I just mentioned it in the above post RIGHT BEFORE I confessed my confusion). There was data there right before progmem string data I recognized (and was labeled with the variable name), and since I couldn't figure out what that data would be for, and the ctors label looking at me, I assumed it was ctors data (which I should never see, I think, unless using c++). Well, I just remembered I had some binary data in an array that was set to use progmem.

I hear my rock calling me. Time to crawl back under it.

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

Curt, thanks very much for your help -- much appreciated.

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

condemned wrote:
Take care if you've got a (very) slow clockspeed and have the watchdog timer enabled.
Good point. It's nice to have the support in gcc for early code for flexibility.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
  asm volatile("eor r30,r30"); 
  asm volatile("eor r31,r31"); 
  asm volatile("ijmp"); 

Why don't you just JMP to zero?

  asm volatile("jmp 0"); 

C: i = "told you so";

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

Or, for a bit of variety:

asm volatile("ldi r16,0");
asm volatile("push r16");
asm volatile("push r16");
asm volatile("ret");

:lol:

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

A macro would do the trick to take care of avr's that don't have jmp-

#if __AVR_MEGA__
asm volatile("jmp 0");
#else
asm volatile("rjmp 0");
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For those diehard people who want to make everything in C:

...
    void (*reboot)(void) = (void(*)(void))0;

    ...
    if (newconf != eeconf)
    {
        EEPUT(newconf, 8);
        PUTS("EEPROM updated, going to reboot now!\n");
        for (i = 0; i < 1000; i++)
            delay_us(1000);
        reboot();
    }
    else
    {
        PUTS("No changes made to EEPROM.\n");
    }

Of course, the 1-second delay is only there since people will for sure expect
a reboot to take some time. ;-)

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

The C thing is good, assuming you don't care about the pc that was just stored on the stack, which most of the time you wouldn't care about when doing something like that.

I think that #if __AVR_MEGA__ could also be replaced with #if (FLASHEND > 0x1FFF) // > 8k

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

And here is the C ijmp-

goto *0;

I was just screwing around, it ends up clearing r30/r31 then ijmp, for both '88 and '168. I wish I knew that earlier.

(I saw this stuff in 'labels as values' before, and it now dawned on me that we already know the 'label address' is 0, so why not goto 0)

and an ijmp to some function-
goto *((void(*))some_func);

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
printf("Please cycle power.");
cli();
for(;;);

C: i = "told you so";

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

cpluscon wrote:
Why don't you just JMP to zero?

  asm volatile("jmp 0"); 

That instruction is not supported on the ATTiny I'm using.

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

Thanks, I didn't know that!

C: i = "told you so";