The never ending (main function) story

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

Having had some success with I2C code, I am now scratching my head over why a main function (with no return in it!) should execute endlessly?

This code:

void
main(void)
{
  uint16_t a;
  int rv;
  uint8_t b[16];
  uint8_t x;

  ioinit();

  stdout = &mystdout;
  printf("\nstarting...\n");

  for (a = 0; a < 256;)
    {
      printf("%#04x: ", a);
      rv = ee24xx_read_bytes(a, 16, b);
      if (rv <= 0)
	error();
      if (rv < 16)
	printf("warning: short read %d\n", rv);
      a += rv;
      for (x = 0; x < rv; x++)
	printf("%02x ", b[x]);
      putchar('\n');
    }

#define EE_WRITE(addr, str) ee24xx_write_bytes(addr, sizeof(str)-1, str)
  rv = EE_WRITE(0x80, "The quick brown fox jumps over the lazy dog.");

  if (rv < 0)
    error();
  printf("Wrote %d bytes.\n", rv);
  for (a = 0; a < 256;)
    {
      printf("%#04x: ", a);
      rv = ee24xx_read_bytes(a, 16, b);
      if (rv <= 0)
	error();
      if (rv < 16)
	printf("warning: short read %d\n", rv);
      a += rv;
      for (x = 0; x < rv; x++)
	printf("%02x ", b[x]);
      putchar('\n');
    }
  printf("done.\n");

}

runs endlessly when I would expect it to run just once.

Dean

Last Edited: Fri. Jun 1, 2007 - 06:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
when I would expect it to run just once

But this is an embedded system, not a pc. In a pc when main finishes, it returns to the operating system. But in an embedded system, there is no operating system. So when main ends, there is nothing for the system to do but re-enter main and run it again. You always need to make sure main never ends.

Regards,
Steve A.

The Board helps those that help themselves.

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

Ah!

So maybe I just add:

for( ;; ){ }

to the main() function, that seems to work!

Thanks, Dean

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

Yes, that "for (;;) {...}" (or while (1) { ... }) is a rather constant idiom you'll see in embedded programming.

The only exception I've seen is in a PIC microcontroller where the default is that after main() finishes, then a RESET vector interrupt is called which restarts main(). But, even on PIC programs, that implicit RESET after main() exiting is rarely used since before then main loop (ie, while (1) { ... }), there's a fair bit of setup that doesn't need to be re-executed.

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

Just to note that GCC used to have a "catch all" "loop: rjmp loop" (IOW a for(ever)/while(1)) beyond main()

But in the WinAVR20070122 release the patches that were applied broke this behaviour.

At the time it was noted as an error and there was a promise that it "would be fixed in the next release". That release (WinAVR20070525) has now occurred but I haven't checked yet whether that catch-all while(1) has been reinstated

But I guess one shouldn't RELY on this (for the exact reason just described) and it makes sense to always end main() with an infinite loop anyway

(in fact most embedded programs would be designed this way anyway - they are usually intended to continue doing "the job" until the end of time - the possible exception being those where initialisation is done, interrupts are enabled then all the "work" is done in ISR()s but this still needs a while(1) in main to soak up the "idle time")

Cliff

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

PS just checked a .lss from a WinAVR20070525 build and the entry to main() is now:

  86:	0e 94 49 00 	call	0x92	; 0x92 
8a: 0c 94 52 00 jmp 0xa4 ; 0xa4 <_exit>

and _exit is:

000000a4 <_exit>:
  a4:	ff cf       	rjmp	.-2      	; 0xa4 <_exit>

So the catch all while(1) *IS* back. :)

(actually I was browsing the top of the avr-libc CVS the other day and I think I spotted that this change was actually done by our very own Jörg)

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

So if I understand correctly, it will stick in the ‘exit’ loop, and only leave it if there’s some kind of interrupt? Also it looks like the program will never reenter the main loop (at least if there are no weird jump’s in one off the ISR’s)?

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

Exactly, the main function will be called (it is a normal function in the latest WinAVR release, and can be called as such) and upon return an infinite loop will be entered. Main will never re-enter until the AVR resets.

- Dean :twisted:

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

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

Note that you are free to implement exit() the way you want. ;-) The
librarie's exit() is only taken if you don't supply your own.

The current behaviour is compliant with the C standard requirements,
albeit the patch from Björn Haase (that makes main() a normal
function, it came bundled with his ATmega256x patch so we initially
didn't notice) has been debated in the past as it causes additional
overhead. So if you don't want to have the additional overhead, the
suggestion is to use

int main(void) __attribute__((naked,noreturn));
int
main(void)
{
  /* ... */
  for (;;) {
    /* ... */
  }
}

Note that this causes a warning about a noreturn function returning,
ignore that by now. A better approach has been discussed but not yet
implemented. In effect, it will be an attribute combining naked and
noreturn, yet still allowing for a return value to be passed to
exit(). This is comparable to IAR's "__c_task" declarator.

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

Do not understand completely the meaning of all this (did not search for that ATmega256x topic yet), but does it have something to do with RTOS’s?

Another question, I saw in some program’s (from Atmel) another end program instruction; RETURN 0, what does this implement?

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

fleemy,

It's got nothing to do with RTOS's!

But the "return 0" is kind of the whole point of this thread. The C standard mandates that main() should have an int return value. On Windows/Linux/etc. this makes sense because the main() programs you write DO hopefully finish at some point and when they do they might want to return a value to the operating system to say how things worked out. But that model doesn't make much sense in an embedded 8 bit microcontroller - nothing "launches" the AVR program and there's nothing for it to "return" to. yet, for compliance, main is given the "int main(void)" prototype. And because it's got a return value you are likely to get a warning from the compiler if you don't include a "return N;" as the last line of the program but the key thing is to ensure that you never actually get as far as that line because if you hit it and main() returns the big question is "to where?".

(actually the answer is "to some code that then jumps to _exit()" but that's another story - and, indeed, the very subject of this thread)

Bottom line is that your average avr-gcc program is going to (usually) have a structure along the lines of:

#include 

int main(void) {

  init_functions();
  while (1) {
    do_the_work_functions();
  }

  return 0;
}

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

Sorry to bother again, but if I understand correctly;
If I start the main routine as follows:

int main(void) {
    While(1){
        do_some_things();
        If (unexpected error) break;
    } 
Return 0;
}

This will compile correctly and if there is some kind of unexpected error it will jump out of the While(1) loop to the return 0 or ‘exit’ loop.
But now, as I see lots of code starting the main routine as follows;

void main(void) {
    While(1){
        do_some_things();
        If (unexpected error) break;
    } 
//-- cannot use Return 0 because main cannot return a value ;
}

This will also compile correctly and the compiler will add the ‘exit’ loop if the program jump’s out of the while loop?
Should we not always use ‘int main(void)’ combined with return0, instead of ‘void main(void)’? Or I am I still missing something?

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

You get the _exit loop (unless you over-ride it as Jörg suggested) whatever you do.

But in your program, supposing you hit this "unexpected error" condition, what were you hoping would happen? Do you want the AVR to "lock up" and just sit in a tight "label: rjmp label" loop? Or do you want the AVR to reset?

Either way I wouldn't break from the while(1) anyway. I'd make the clause of the if() condition be either code to enable the watchdog and then while(1) if I wanted a reset in the condition or I'd just simply make it a while(1) so it was an infinite loop in my control and not something I was relying on that may or may not be there beyond the return from main()

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

The C standard declares main as returning an int. With some versions of WinAVR, you'll get a compiler warning if you declare main as void. Thus, I delcare main as returning an int and add a "return 0;" as the last statement of my function.

On the other hand, that is only to eliminate compiler warnings. The 'forever' loop in main never exits. If something "unexpected" were to happen, I'd either display "Unexpected error. Halting" on a display device then then go into a sleep mode or jump to the beginning of the main function and reinitialize the system.

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

kevinrosenberg wrote:
jump to the beginning of the main function and reinitialize the system.

Presumably you actually mean "invoke the watchdog" to make the "jump to main"? Any other technique (such as a JMP 0) will leave all the function blocks in the AVR (timers, ADC, TWI, SPI, ...) in their currently programmed state so, unless your initialisation stuff resets ALL the registers, you may have something set in an unexpected way at the restart (and you'd want to throw in an cli() along the way somewhere too!)

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

Sure, I do not want my program to stop and stick in an infinite loop if some 'unexpected error' would happen, it was only as an example for exiting the while(1) loop. My question was more why some people are using 'void main(void)' and others are using 'int main(void)', but I get the idea behind it now.

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

Most of the other AVR compilers that are targetted to the AVR or embedded work define main() to return void but avr-gcc is just one of 100+ ports of the core GCC and it needs to cover all architectures, some of which would prefer an int return from main(). That's the only reason it's like that (and because GCC adheres closely to the C standard while others are a bit more fluid in their interpretation of it)

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

Actually, GCC only warns about when running in "hosted environment"
mode (-fhosted) which is its default, as the C standard requires
main() to have one out of two well-specified prototypes in this mode.
When using "freestanding environment" mode (-ffreestanding), main()
can have any prototype you like.

However, while a microcontroller application at a first glance appears
to be a classical case for a freestanding environment, a closer look
will make it evident that this is not really as much the case: using
-ffreestanding will prevent the compiler from performing any kind of
optimizations that are based on intimate knowledge of the C standard
library, so e.g. using

l = strlen("Hello, world!")

will result in a run-time call to function strlen() while in hosted
mode, it will eventually be replaced by the constant 13, with
subsequent optimizations possibly even eliminating the existance of
variable `l' alltogether. So in practice, as you *are* using a subset
of a C standard library for AVR-GCC applications, they are neither
clean cases of a hosted nor of a freestanding environment. In most
cases, you'll probably yield best results by sticking to the -fhosted
default, declaring main() as return type `int', yet letting main() end
up in an infinite loop and no trailing return statement which will not
be warned by the compiler as the code analysis will eventually tell it
that this main() cannot return anyway.

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

Ok, thanks.

Edit: It may seem not such a big deal, declaring main as int or not at all, it will not affect the program by itself. But I like to know exactly why you do so and why not, after all it’s the little things that make the difference.

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

Thanks for the additional clarification, Jörg.

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

IIRC amidst the CRS fog (and with Search broken I couldn't dig deep enough to verify) the was (is?) a problem with (certain?) AVR models, interrupts, and the tight single-instruction loop as described as the catch loop. The solution was to have a NOP-RJMP loop.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.