I'm doing something strange!

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

Hello Fellow Freaks,

 

I'm using an Atmega328PB xplained-mini and connected a switch that breaks me out of the software, regardless of where it is. 

Obviously, there is no non-maskable interrupt on this processor, so I just used a normal interrupt. 

 

When the switch is pressed and debounced, I simply jump to address 0x0000.

I know I could use the Watchdog to Restart in the event the software hangs, but this gives me the ability to simply Restart the program at any time, even while it's running normally. 

 

This works fine for my testing purposes and definitely NOT something I would leave in the finished product.

 

So here are the questions:

1. When a program restarts from address 0x0000, does the stack pointer get reset?

2. How do I pop the return address from the interrupt off the stack pointer before the jump in "C"?

3. If I jump to 0x0000 when using Debug Wire for debugging, am I going to leave a fuse in the wrong state, like cycling power would.

 

Thanks for your help and suggestions,

Paul

 

 

 

 

 

 

 

You never know where life is going to take you; sit back and enjoy the ride!

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

PaulieT wrote:
1. When a program restarts from address 0x0000, does the stack pointer get reset?
Depends on the program. In Asm it's up to you. In C it depends on the "C Run Time" (the bit of C that runs before you reach main()). I think the CRT in most compilers have stack initialisation code - so expect it to be put back to RAMEND.
PaulieT wrote:
2. How do I pop the return address from the interrupt off the stack pointer before the jump in "C"?
Easiest would be to implement the ISR in Asm then you are in complete control. If you write the ISR in C then you need to know what the compiler is likely to do in the prologue. In GCC for example you will find R1 and SREG pushed to the stack. After that there may be more PUSHs depending on which other registers are also used in the ISR. if this is about getting "where was I when the interrupt hit" then you need to look at an Asm listing of the ISR and see how many things are on the stack between the return address and the current SP in the core of the ISR. In GCC for example "SP" is defined as a "variable" so you can just read it to a uint8_t pointer and then read *(spCopy - 5) or whatever to access bytes of the return address on the stack.
PaulieT wrote:
3. If I jump to 0x0000 when using Debug Wire for debugging, am I going to leave a fuse in the wrong state, like cycling power would.
This has nothing to do with DWEN. It is set when you start debugging and it is reset when you use "stop debugging" on the debug menu. Nothing you do in the "running code" can have any influence on the state it is in.

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

??? Why not just have a reset button/switch?

 

1.  A trick question.  No, when you jump to 0 nothing is "reset".  It is just like a branch to any address.  However, depending on your language and toolchain, your startup code may well set the stack pointer.  There are prior discussions on this.

 

2.  Why do you care, if the stack is going to be reset by your toolchain anyway?

 

3.  I can't answer that.

 

IMO do a "proper" reset whenever practical.  For example, suppose you had a active output that is pumping chemicals.  Jump to 0 and it keeps going and going and going like the Energizer bunny.

 

 

 

 

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

In avr-gcc, if you want to interject some code before the normal CRT executes, you can use an .init0 section, for example:

__attribute__ ((naked, section(".init0")))
void Reset_Vector_Will_Jump_Here(void) {
    __asm__ volatile (
        // Insert assembly code here, NOT C. 
        // The device is not yet setup to run C code...
    );
    // When this routine exits, normal CRT will execute
}

 

David

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

I assume you are writing in C here?

 

You seem to have reached a point where it is usefull to know a bit more about the workings of a C compiler.

To make **ANY** C program work not only your source code needs to be compiled (and linked if multiple files) but some extra magick has to be added into the mix.

 

That "extra magic" is (at least ?) 2 files.

1). a Linker script.

2). Startup code.

3). Libraries (For example for floating point if it is used and your AVR)

 

1). The linker script has info such as the amount of Flash and Ram for your particular uC. You get errors if you try to use too much Flash, and the amount (and adresses) of RAM are needed to calculate a valid initialsiation point for your stack pointer.

On my system I can find the linker script for my particular AVR in files such as:

/usr/lib/avr/lib/ldscripts/avr5.x

 

2). The startup code is a file usually written (partly) in assembly, but might also have C code.

This file holds code which is executed before the main() is called.

It copies static data from Flash to Ram and it loads the stack pointer with an address which is calculated from the information in the linker script (or similar)

I could not find source for the startup code on my 'nix box. I suspect those files are pre-assembled and in directories such as:

/usr/lib/avr/lib/avr5/

 

3). Some of those extra libraries are in:

/usr/lib/avr/lib/avrxmega2/libm.a

/usr/lib/avr/lib/avrxmega2/libprintf_min.a

 

==================

But you seem to be thinking about this problem from the wrong point of view.

When you simply jump to 0x0000 you AVR simply executes code just as it would after a hard reset.

Your C compiler takes care of proper initialisation of anything in RAM and some registers such as the stack pointer.

The problem is however that your C compiler also depends on the default values of a lot of the internal registers.

Initialized timers will keep on running, DDRx bits are non default, USARTS will keep it's state, interupts will stay enabled etc, if you simply jmp to 0x0000.

 

The idea of trusting on a WDT timeout is that it resets all AVR registers to it's reset state.

Being to lazy to spend half an hour to add that code to your program is ... not so smart (probably).

 

If this is really what you want and you want to do it by design then you should make no assumptions about any hardware registers of your AVR.

If you want to initialize a timer in your code, then initialize all registers of the timer. Even (especially) all registers which have the default values.

 

You could also make a table of all AVR registers and write them to all registers in a loop (Put it in the ".init section"), but just using a WDT timeout is easier and better).

 

Note:

1). There is also a ".noinit" section. Data here is left uninitialized by the C compiler and can be used to store variables which can "survive" a hardware reset (but not a power cycle).

2). You can examine MCUCSR for the source of an reset event for special initialisation.

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

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

theusch wrote:
IMO do a "proper" reset whenever practical.

Absolutely!

 

As already noted, jumping to the reset vector does not do a real reset - it does not reset any of the hardware.

 

If you want a reset button, why not just connect it to the reset pin?

 

Or you could have your "reset" button deliberately cause the WD to fire.

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

frog_jr wrote:
before the normal CRT executes, you can use an .init0 section,
Err no! The user manual:

 

http://nongnu.org/avr-libc/user-...

 

tells us that the sections free for "user" code are the odd numbered ones, .init1, .init3, .init5, .init7 (and rather curiously also .init8). You shouldn't really use .init0. If you have code to add "early" then put it in .init1 or .init3

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

Thanks everybody for your insight and suggestions.

 

It's good to know that the C compiler takes care of proper initialization of anything in RAM and the stack pointer when I jump to 0x0000.

Not so good about the hardware not being reset. 

 

The suggestions about using .noinit Section and the .initN Section in the Memory Sections of AVR LibC were really helpful. I'm going to try .init9 which jumps to Main(). 

 

theusch suggested using the Reset pin. I tried this early on, it goes somewhere (probably the boot loader!) but did not restart my code.

I suppose I could watch where this goes using the ICE, but I have not done that yet. 

 

I'm going to re-think what I am doing.

 

Thanks again,

Paul

 

You never know where life is going to take you; sit back and enjoy the ride!

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

I know I could use the Watchdog to Restart in the event the software hangs, but this gives me the ability to simply Restart the program at any time, even while it's running normally.  

 

You realize, you can still do this with the Watchdog.

You just have the ISR you are using now enable the WDT and 16 mSec later the system does a formal reset.

You can sit in a tight Do Loop while you wait the 16 mSec, if desired, so that no other code is executed, if that makes a difference to you.

 

Overall I'm still not certain what you gain by using this approach over just having your push button switch tied to the micro's Reset\ pin.

(Typically with a 10K resistor and a 0.1 uF cap).

Resetting the micro to a well defined start-up state is exactly what an activation of that pin does, and as you eluded to, it doesn't matter if the micro is running the code properly, or is off lost in the weeds somewhere.

 

I'm all for innovative and creative uses of the micro and its hardware, but it would seem that you are going out of your way not to use a feature that is already present, and easily accessed, perhaps without any additional feature or capability being created by your approach.

 

JC

 

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

PaulieT wrote:
Not so good about the hardware not being reset.
That's why you want a "harder" reset. The most obvious ones are either simply a button on the _Reset line as others suggested above (seems the most obvious to me!) or you need your button action to invoke a watchdog reset. That is just like a power on reset EXCEPT that different reset flags are set in the MCU status register as it comes out of the reset - but all the peripherals have all their registers set to defaults (on the whole "all off").
 

But I still don't understand why you don't simply have a push button on the _Reset line? Is this about giving the code a chance to do some kind of "last minute shutdown" thing (store to EEPROM?) before it goes into the reset phase? If that then a button that is detected and triggers the WDT (after shutdown action) would seem like the way to go.

 

A chip that has BOOTRST active will always reset into the bootloader. I't up to the bootloader logic to then make the decision (maybe based on a pin input - perhaps a button or perhaps some value in memory or EEPROM?) to not stick with bootloading but to just carry on to the app code (effectively a JMP 0)

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

PaulieT wrote:
theusch suggested using the Reset pin. I tried this early on, it goes somewhere (probably the boot loader!) but did not restart my code. I suppose I could watch where this goes using the ICE, but I have not done that yet.

C'mon.  How ridiculous does that sound?  A jump to 0 "restarts your code", but pulling /RESET low does not?!?  Every AVR app with a reset button really doesn't work?  I'd suggest you investigate this phenomenon.  Does /RESET really pull low?  And then what are the symptoms?

 

-- It could be that you are using the pin as an I/O and the RSTDISBL fuse used.

-- ...and you mentioned the bootloader.  I guess, as that model has alternate vector location, it could indeed be the bootloader.  I'd think the bootloader would have an "escape mechanism".

 

???  You bootloader experts will need to enlighten me as to the IVSEL -- OP's jump to 0 bypasses that mechanism?  And one keeps IVSEL active during normal operation?

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

Lee, as I say a well written bootloader might well start at the BOOTSZ boundary and it might even do an IVSEL (God forbid!) but the point is that once it's made the "I'm not doing the bootloady thing today" it should put back anything it has touched (perhaps even a WDT reset back into itself and a study of WDRF to know it meant it?) then a JMP 0 so the application is oblivious to the fact it wasn't a power on reset to 0.

 

Seems to me his problem may be a fault in his bootloader's reset logic.

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

In post #7,

clawson wrote:
Err no!
Well, I beg to differ, although I really don't want to do so when clawson has spoken!.smiley

In the referenced page when talking about "The .initN Sections", I may be missing it, but I see nothing suggesting that .init0 should not be used (or any of the other initN sections).

 

From my experience:

Both .init0 and .init1 place code prior to any CRT (i.e. the reset vector points to the first of these).

.init2, .init3 and .init4 place code just after stack initialization and prior to copying data from flash to SRAM.

.init5, etc. place code after variable initialization and the clearing of the .bss,  prior to the call to main().

 

I very rarely use the .initN sections; however, if I am wrong I would truly like to know.

 

Edit: typo (sorry about mangling your name clawson)blush

David

Last Edited: Wed. Feb 28, 2018 - 04:30 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think it must have been Joerg who originally told me this (perhaps 10 years ago?). I have a feeling I posted something that used even numbered init sections and he said the even numbered (including 0) ones were for the CRT and the odd numbered ones were for the user to "insert stuff".

 

If both CRT and your own code put stuff in .init0 then it's presumably simply a question of link order as to which "gets in first". It may or may not matter to you whether you are before/after the SP setting code.

 

EDIT: sorry I am wrong about SP code - that is in .init2, not .init0. In fact as far as I can see the CRT does not put anything in .init0 so I guess it doesn't matter whether you use .init0 or .init1 (with the proviso that some later model/CRT might have some reason to put something in .init0 I guess?)

 

EDIT2: actually reading a .x file it seems the C++ ctors and dtors are placed before the .initN sections but I suppose that doesn't matter as the jump at the start of .vectors is presumably to a label in .init2

 

EDIT3: OK, so wrong again:

http://svn.savannah.gnu.org/viewvc/avr-libc/trunk/avr-libc/crt1/gcrt1.S?revision=2519&view=markup#l200

so the destination of the XJMP in .vectors is .init0 though the only thing in it is the label, not the code

Last Edited: Wed. Feb 28, 2018 - 04:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Seems to me his problem may be a fault in his bootloader's reset logic.

It was hard to divine OP's intent at the start.  Perhaps the questions had to do with stack manipulation for some kind of profiler interrupt.  Or perhaps doing a "come from" with a locked app.  But further discussion seemed to belie those guesses.  IME I think he is making it too hard -- why a reset "doesn't work" should be figured out IMO.

 

If the intent is to "start clean" I'll keep voting for a real AVR reset.

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

theusch wrote:
why a reset "doesn't work" should be figured out IMO.

+1

 

If the intent is to "start clean" I'll keep voting for a real AVR reset.

+1

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...