Pages: [1] 2   Go Down
Author Topic: Bootloader and MCU reset register  (Read 3023 times)
0 Members and 1 Guest are viewing this topic.
Switzerland
Offline Offline
Sr. Member
****
Karma: 6
Posts: 375
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'd like my application to be able to distinguish a power-on reset from an external reset.  It looks like the MCUSR register does what I want, except that the bootloader wipes this register when it runs:

Code:
  // Adaboot no-wait mod
  ch = MCUSR;
  MCUSR = 0;
  if (!(ch & _BV(EXTRF))) appStart();

Can I safely replace these lines with the following?

Code:
  if (!(MCUSR & _BV(EXTRF))) appStart();
Logged

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 206
Posts: 12852
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


I can't answer your question but I can, hopefully, give you a word of advice.

The risk with the modification is that the board may get stuck in a loop or the bootloader may not run when it should.  I suggest a mental walk through the code with various MCUSR bits set under a few common scenarios.
Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Can I safely replace these lines with the following?

Code:
  if (!(MCUSR & _BV(EXTRF))) appStart();
I would say yes - this is a safe mod that will allow you to test for power on or reset in your sketch. It gets a bit more complicated if you want to add support for watchdog, but otherwise not.

Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 132
Posts: 6746
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hmm.  The clearing of MCUSR comes from the ADAboot bootloader, and I'm not sure what it accomplishes.
Note that a normal "reset" that goes through the bootloader (button, auto-reset) will end up showing that the MCU was restarted by a watchdog timeout, rather than an external reset.
Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Note that a normal "reset" that goes through the bootloader (button, auto-reset) will end up showing that the MCU was restarted by a watchdog timeout, rather than an external reset.
That was cryptic …?

I've used the above mod for quite some time and my experience is that EXTRF is the only bit set (watchdog disabled) after a normal reset and so generally allows for cause of restart to be determined (power-on, reset or brown-out). This behavior is also supported in the documentation of MCUSR (AtMega328). The bits are sticky however and so the cause of a second restart (unless power is cycled) may not be correctly determined unless MCUSR is explicitly cleared.

Logged

Switzerland
Offline Offline
Sr. Member
****
Karma: 6
Posts: 375
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for the great info guys.  P54 of the ATmega datasheet indicates that the MCUSR flags have to be explicitly cleared between resets.  As far as I can see it is safe to do this in the application code: the worst that can happen is that the application fails to clear EXTRF before a non-external reset, causing the "no-wait" check to execute the bootloader needlessly.

I haven't quite wrapped my head around the Reset vectors yet.  Reading the Optiboot v44 code, if the bootloader receives no readable character within 1s, then the watchdog induces a system reset.  But with BOOTRST programmed, why does this cause the application to run rather than restarting the bootloader?
Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 132
Posts: 6746
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
The bits are sticky however and so the cause of a second restart (unless power is cycled) may not be correctly determined unless MCUSR is explicitly cleared.
Really?  That would be a problem, then.
The bootloader "decides" to run the user sketch by letting the WDT expire and reset the CPU, and the bootloader only runs if the reset reason was "External Reset."  (so the sketch starts "immediately" for poweron or after WDT Reset.)
So the "normal" sketch startups are:
  •  Power on, start sketch.
  •  Reset switch, start bootloader (enabled WDT), timeout causes WDT reset, start sketch.
If the bit is sticky as you describe, the second example would probably loop:
Reset switch, start bootloader, WDT Reset, (still sees EXT RESET), start bootloader, WDT...
I'm not quite sure I believe the stickiness works like that.  (It COULD be "sticky" in the sense that you could end up with multiple bits set in MCUSR.)
It should be safer to move the clear of MCUSR after the decision to run the bootloader.

(note that the older (2K) bootloader had a different startup mechanism.)
« Last Edit: June 21, 2011, 06:39:04 pm by westfw » Logged

Switzerland
Offline Offline
Sr. Member
****
Karma: 6
Posts: 375
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Aaaah, now I understand.  So to avoid looping I need to write
Code:
  // Adaboot no-wait mod
  ch = MCUSR;
  if (!(ch & _BV(EXTRF))) appStart();
  MCUSR = 0;

My application will then be able to read the reset status, except that External resets will appear as Watchdog resets.
Hmm.  What about storing MCUSR in another register, such as GPIOR0?
Code:
  // Adaboot no-wait mod
  GPIOR0 = MCUSR;
  MCUSR = 0;
  if (!(GPIOR0 & _BV(EXTRF))) appStart();

Time for some experimentation.  Thanks again,
Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 132
Posts: 6746
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

An advantage of the way optiboot starts the sketch is that the sketch starts with the microcontroller with the state REALLY REALLY CLOSE to having everything in the "immediately after reset" state (which may or may not be what you expect, as Uno SMD discovered.)  Moving the clear of MCUSR would make that even more true, which I would normally count as "good."  Assuming no other side effects.
Logged

Switzerland
Offline Offline
Sr. Member
****
Karma: 6
Posts: 375
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

OK, so this doesn't work:
Code:
  // Adaboot no-wait mod
  ch = MCUSR;
  if (!(ch & _BV(EXTRF))) appStart();
  MCUSR = 0;
The watchdog is still running as the application starts, and resets it after a few ms - causing an endless loop.  Page 55 of the datasheet explains why:
Quote
WDE is overridden by WDRF in MCUSR. This means that WDE is always set when WDRF is
set. To clear WDE, WDRF must be cleared first.

This does work, at the cost of a couple of bytes of bootloader space:
Code:
  // Adaboot no-wait mod
  ch = MCUSR;
  MCUSR = 0; // required to prevent the bootloader from looping and the watchdog from biting
  GPIOR0 = ch; // <--- extra code
  if (!(ch & _BV(EXTRF))) appStart();
It goes slightly against the philosophy of having the bootloader interfere as little as possible with the state of the CPU, but all the IO registers are wiped anyway after a reset (p46 in the datasheet).

On powerup both the brown-out and power-on flags are set.  Presumably the brown-out condition is set as the power is removed and the on-board capacitors discharge, and remains when power is restored.  After an external reset, the watchdog-reset flag is set as expected.
Logged

Boston, MA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 83
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I haven't looked at MCUSR so am not advocating one way or the other but FYI, a reset does not wipe RAM like a power on/power off does.  So you can squirrel away something in RAM and then check it later (so long as the bootloader does not clear RAM just for the fun of it).  I used this trick in my
"toastedboot" loader to implement different behavior on a "double-click" of the reset button.

Andrew
Logged

http://www.toastedcircuits.com Lightuino LED driver: 16 sources, 70 sinks, remote controlled.  Also high powered LED drivers.

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 132
Posts: 6746
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
a reset does not wipe RAM like a power on/power off does.
I'm pretty sure that even a power-on reset is not in any way guaranteed to leave RAM in any particular state...
(Nor register contents, as discovered with the Uno SMD bootloader looping problem.)
(the only things you should assume have a known state after reset are the IO registers documented as having an initial state in the data sheet.
Logged

Boston, MA
Offline Offline
Jr. Member
**
Karma: 0
Posts: 83
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Please excuse my quick summary:  To be extremely precise, I was using "wipe" not in the sense of "clear" but in the sense of "wipe out" or "destroy what is currently there".  Therefore if you placed a well known but random (flip a coin 32 times) 32 bit integer that is not 0 or 0xffffffff (just in case the CPU does wipe-as-in-clear) into one memory location and your data into another, then IF that well-known integer is there when the sketch begins, it is extremely likely to be a soft reset.  Note that this will only work if the bootloader does not use that memory location itself (or if you ARE the bootloader, or if you dispense with it entirely by doing ICSP programming, which has the added advantage of starting your sketch right away).

Cheers!
Andrew
Logged

http://www.toastedcircuits.com Lightuino LED driver: 16 sources, 70 sinks, remote controlled.  Also high powered LED drivers.

Switzerland
Offline Offline
Sr. Member
****
Karma: 6
Posts: 375
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thinking about the Optiboot reset logic a little more...  After an External reset the bootloader runs, executes a Watchdog reset, runs again, and starts the application.  Is there any objection to having the WatchDog call appStart() via an interrupt, avoiding the extra reset and the need to clear MCUSR?  The "Watchdog Interrupt Enable" flag is apparently cleared automatically, avoiding any risk of creating a loop.

The following produces a 568-byte binary.  Any ideas for a more efficient way of setting the interrupt vector?
Code:
158a159
> #include <avr/interrupt.h>
289d289
<   MCUSR = 0;
654c654
<   WDTCSR = _BV(WDCE) | _BV(WDE);
---
>   WDTCSR = _BV(WDCE) | _BV(WDIE);
655a656,659
> }
>
> ISR(WDT_vect) {
>   appStart();
Logged

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 206
Posts: 12852
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Is there any objection to having the WatchDog call appStart() via an interrupt

Yes.  The watchdog interrupt does not reset / clear the processor.
Logged

Pages: [1] 2   Go Up
Jump to: