theoretical questions about infinite looping in main

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

From what I have been taught and in every example I have seen, uC firmware always sticks to the following format:

 

//globals, macros and library declarations
main() {
    //some initialization stuff
    while(1){
        //loop forever performing some tasks
    }
}

I was wondering, what happens if you do not include an infinite loop?

 

Does the program counter just stop or will it roll over and eventually restart the entire program?

 

If it does stop are interrupt routines still serviced?

 

If it does restart the program, what happens to global variables?

 

Is there any example of an application that does not use an infinite loop?

Last Edited: Sat. Oct 8, 2016 - 01:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What happens when a train runs off the track?
if you want the processor to stop, you put it too sleep. But if you wake it up, it needs instructions to execute. Interrupts are normally used to wake the processor from sleep.

Will the program counter roll over? Possibly, but the end of main() isn't necessarily the end of the code. There may be other functions following it.
If we assume the program memory is filled with nops, then the program counter will roll over. Interrupts will be serviced unless they are disabled. When it doeswrap around, it will execute the C startup code and probably zero out your globals.

In short, you need the infinite loop.

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

What happens depends on the compiler.

 

I believe, but could be  wrong, that avr-gcc includes a hidden "trap loop" if the main() function ever exits. No, the  program counter cannot stop (in an AVR anyway, when it is not sleeping). I suspect that interrupts MAY remain active but that will be compiler dependent. It will restart the program only if the watchdog expires and resets it and THAT depends on whether or not you enable the watchdog. Others will have to tell you what happens to variables on a reset.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

The main (no pun intended) point is this:

 

On a PC there is an operating system running in the background.

 

When a program, be it MS Word, Solitaire, or anything else, exits, the system falls back onto the operating system.

 

Most small microcontrollers aren't running an operating system, the program fully defines the system's behavior, there is nothing to exit to.

 

JC

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

futz wrote:

I was wondering, what happens if you do not include an infinite loop?

 

It depends on the run-time code. There is a file typically called startup.s or startup.c that sets up the C/C++ environment, then calls _main. In some startup code I have looked at for embedded systems, there is a while(1) equivalent after the call to main.

If you hit this "backstop", interrupts will probably still be serviced. Without the backstop, the CPU will go off executing random code. which really could do anything. Even unmapped memory can be a valid instruction, so the CPU could execute to 0xFFFF then get back to 0x0000 and appear to reset. (I have seen this happen!)

 

Do you have a specific compiler-CPU in mind?

Bob. Engineer and trainee Rocket Scientist.

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

All of the above answers are valid and correct, but if I wanted to know something like this, I would just look at the generated .lss file and see what code is there, so I did:

  74:	0e 94 40 00 	call	0x80	; 0x80 <main>
  78:	0c 94 41 00 	jmp	0x82	; 0x82 <_exit>

0000007c <__bad_interrupt>:
  7c:	0c 94 00 00 	jmp	0	; 0x0 <__vectors>

00000080 <main>:
int main(void)
{
    /* Replace with your application code */
    while (1) 
    {
    }
  80:	ff cf       	rjmp	.-2      	; 0x80 <main>

00000082 <_exit>:
  82:	f8 94       	cli

00000084 <__stop_program>:
  84:	ff cf       	rjmp	.-2      	; 0x84 <__stop_program>

The C startup code "calls" main and if ever main returns, the program will jump to _exit where the processor turns off interrupts and then just spins executing the jump to __stop_program.

 

Your compiler may vary (YCMV)wink

David (aka frog_jr)

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

If you're writing code in assembler, I believe it just runs off into unprogrammed memory.  The default unprogrammed flash parses as 'jmp 0x0000', and so it starts over.  This is just a first opinion - Must research to ensure accuracy and correctness.

 

S.

 

PS - That the CPU parses unprogrammed memory as 'jmp 0x0000' may be no accident.

Edited to add postscript.  S.

 

Last Edited: Sat. Oct 8, 2016 - 11:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Unprogrammed flash is NOT "JMP 0x0000". It is 0xFFFF which is not a valid opcode but it executes as SBIS R31, 7 usually that bit is not set so it's effectively NOP. Those continue to the end of flash then wrap back to 0x0000. So,  yes,  executing unprogrammed flash eventually gets you back to 0x0000 but it's not a JMP. 

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

I take my helmet off to clawson who knows better than I do. 

 

Still, even if the instruction is skipped, it's still off onto the rest of the unprogrammed flash, and eventually the program counter wraps and you're back to 0x0000 again.

 

So that's what happens if your program has no infinite do-loop and if your compiler doesn't put one in.

 

S.

 

PS - I write programs in assembler, and I have (not deliberately!) caused them to run off into unprogrammed memory.  They restarted, but I didn't test how long it took them to do so.  I also have no idea what AVRs do with invalid opcodes, and for that we have to thank those who know better than we.  Do they do different things with different invalid opcodes?  S.

 

Last Edited: Sat. Oct 8, 2016 - 12:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Scroungre wrote:
If you're writing code in assembler, I believe it just runs off into unprogrammed memory. 

It has nothing to do with what language the source code is written in; the CPU will just keep fetching instructions.

 

But, as already noted, some compilers do put a "trap" after the end of main.

 

DocJC wrote:
On a PC there is an operating system running in the background

Indeed - this is, effectively, the infinite loop!

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The instruction decode unit in CPUs often does not fully decode all potential 256 or 65536 opcode patterns to save decode logic. But it will generally do something. So you often find that the undefined ones act similarly to close bit patterns. In AVR for example the top 4 bits of the opcode generally select a "similar group" so (without looking at the actual detail)  you may find 0000... are branches and jumps, 0001.... are shifts, and that kind of thing. 0xFFFF is close to SBIS and SBIS R31, 7 in particular. 

 

BTW I think it was Freaks previous user "Brutte" who originally spotted this. 

Last Edited: Sat. Oct 8, 2016 - 12:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Scroungre wrote:
So that's what happens if your program has no infinite do-loop and if your compiler doesn't put one in.

No.  Your premise counts on the fact that the last instruction in main() is the last programmed flash word.  The compiler might well "put in" tables or subroutines or library code or similar into program memory space after main().

 

Also, some compilers might call main().  The main() function will have a RET.  Where do you think that might go?

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.

Last Edited: Sat. Oct 8, 2016 - 02:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Unprogrammed flash is NOT "JMP 0x0000". It is 0xFFFF which is not a valid opcode but it executes as SBIS R31, 7 usually that bit is not set so it's effectively NOP. Those continue to the end of flash then wrap back to 0x0000. So,  yes,  executing unprogrammed flash eventually gets you back to 0x0000 but it's not a JMP.
According to the instruction manual, 0xFFFF is valid.  It is SBRS R31, 7 (not SBIS).

Edit: Oops.  Somehow missed the 0 in 0bbb. 0xFFFF is not valid.

That of course raises the question of how the processor handles multiple skips and whether wraparound changes said handling.

The bit is the high bit of the Z pointer, so is usually not set, in which case any number of them should function as a long NOP.

So what happens if the bit is set?

To my mind, if a skip can be skipped, two identical skips should act as two NOPs.

An odd number should act as an even number of NOPs and a skip.

If this behavior is preserved on wraparound, it raises the possibility of a jump to the highest priority interrupt.

If it RETIs, something interesting will happen.

If it is avr-gcc's default, it will do a pseudo-reset.

If the user has been playing games with the reset/interrupt vectors,

wraparound might skip execution of the first instruction of the reset vector and start with the second.

Interesting things will happen.

Of course, anyone who has any business playing games with the

reset/interrupt vectors really should know enough to not return from main.

Iluvatar is the better part of Valar.

Last Edited: Sun. Oct 9, 2016 - 06:00 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As you are so fond of saying "nyet". In the opcode manual SBRS has 0 in bit 3. But this is the redundancy I was talking about. However you are right that I mis-remembered SBIS rather than SBRS. 

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

For some other remarks, no, it doesn't matter what language you program in, but if you use a compiler it might very well put in assembler instructions that you didn't.  When writing assembler, what you wrote is what you get (for better or worse...).

 

theusch wrote:

Scroungre wrote:
So that's what happens if your program has no infinite do-loop and if your compiler doesn't put one in.

No.  Your premise counts on the fact that the last instruction in main() is the last programmed flash word.  The compiler might well "put in" tables or subroutines or library code or similar into program memory space after main().

 

Also, some compilers might call main().  The main() function will have a RET.  Where do you think that might go?

 

Presumably, it will return to wherever the compiler chose to call main() from.  If you want the most brutally effective control over who calls what where, write it out in assembler.

 

You're right in another way, too.  I almost always put string literals, lookup tables, and other rot after the code in the flash memory, and lord help you if the CPU decides to fetch and decode THAT.

 

S.

 

Last Edited: Sun. Oct 9, 2016 - 12:09 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And to go off on a wild tangent, that last remark of mine is an unmitigated violation of the fundamental 'Harvard Architecture' of the AVR itself, in that data should be entirely separate from program, so that data never gets accidentally executed.  Hmm!  S.

 

 

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

Double plus good for the Frog who took the effor to analyse an empty main(){}

Scrounge is just babbling. (No the OP is not writing assembly).

Aso Kudos to Clawson, who knows all.

Scrounge? Wasn't that Scrooge? From some kind of moevie?

Me, I'm just drunk.

 

 

 

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Last Edited: Sun. Oct 9, 2016 - 02:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
As you are so fond of saying "nyet". In the opcode manual SBRS has 0 in bit 3. But this is the redundancy I was talking about. However you are right that I mis-remembered SBIS rather than SBRS.
Sorry about that.  I somehow missed the 0 in 0bbb.

I just did a search for 1bbb.  Didn't find it.  Did find 0bbb.

Iluvatar is the better part of Valar.

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

The program model of main infinite loop of of subroutine calls with interrupts is evidently morphed sometimes into just the while(1){}; loop and everything is an interrupt. I guess this is just a small step from being a task switching operating system. I have seen a couple of program that called all the inits then just did a for(;;){}; and I found this model harder to read and follow than the list of subroutine calls model. 

Imagecraft compiler user

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

bobgardner wrote:

The program model of main infinite loop of of subroutine calls with interrupts is evidently morphed sometimes into just the while(1){}; loop and everything is an interrupt. I guess this is just a small step from being a task switching operating system. I have seen a couple of program that called all the inits then just did a for(;;){}; and I found this model harder to read and follow than the list of subroutine calls model. 

It partially makes sense in a few types of applications.  Take, for example, a DDS ala Jesper's miniDDS.  A cycle-counted loop that runs forever -- when doing the output.

 

But then how do you e.g. stop the output or change the parameters or such? You have the app "upside down" with input/timer/uart interrupts looking for "commands".  As you alluded to, a small step from a task-switching system.  The ISRs might well clean up after themselvs and change the branch return point.  Or maybe actually branch to the right spot to set up the infinite loop with the new parameters and "go".

 

 

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.

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

Scroungre wrote:

And to go off on a wild tangent, that last remark of mine is an unmitigated violation of the fundamental 'Harvard Architecture' of the AVR itself, in that data should be entirely separate from program, so that data never gets accidentally executed.  Hmm!  S.

 

 

The data is separate. The values stored in flash that are used to initialize the data are not separate.

 

I used to add the infinite loop, but I was unable to test it, owing to lack of time.

 

Four legs good, two legs bad, three legs stable.

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

John_A_Brown wrote:
but I was unable to test it, owing to lack of time.
Groan! (very clever though ;-)