Retrieving Optiboot version number

Taking into account that it's at the top of Flash and that Flash size varies, does anybody have any pointers to "best practice" for retrieving the Optiboot version number from the top three bytes?

My ultimate objective is to retrieve the reboot reason from r2 as described at https://forum.arduino.cc/index.php?topic=246359.msg1846568#msg1846568 but Optiboot only started doing this at 4.6.x and I can see at least some Chinese Arduino clones still on 4.4.37 with r2 uninitialised.

MarkMLl

I believe FLASH addressing is modulo. Certainly it is on 16K/32K ATmega168/328 parts.
So, assuming all parts have a power of 2 FLASH size, just read from the highest address available.
On parts with less FLASH the addressing will just wrap around.

Thanks for that, it had occurred to me but but there's also issues related to how to get at the Flash address space and what happens if there are multiple banks.

So far I'm trying

/* This needs a runtime test that Optiboot is at at least version 4.6. If not
 *  valid then the saved resason will be set to an "impossible" value.
 */
bool trustResetReason(void) {
  if (pgm_read_word(0xfffd) >= 0x0406)
    return true;
  else {
    _reset_reason = 0xff; // Not a valid combination
    return false;
  }  
}
#define _RESET_REASON

which appears to work... but is it safe? What is the endianness: should I really be doing this as two separate bytes?

MarkMLl

For ATMega 8 bit devices with more than one bank you'll need to set RAMPZ with the bank before reading the FLASH. I think that should work?

RAMPZ won't be defined on un-banked devices, but you can test for it with a #ifdef...

#ifdef RAMPZ
      RAMPZ = bank;
#endif

That's how optiboot itself does it.

What do you mean "get at the Flash address space"? For what purpose do you need it than to read the version number?

OK, so at the very least I need to note the possibility of handling RAMPZ on some targets.

pcbbc:
What do you mean “get at the Flash address space”? For what purpose do you need it than to read the version number?

Basically, it looks as though it’s available as standard using pgm_read_word() etc.

MarkMLl

MarkMLl:
Basically, it looks as though it's available as standard using pgm_read_word() etc.

Ah, you mean read the contents of the FLASH address space.

Get the Flash address space to me means determining it's range - which is the question we started with.

pcbbc:
Ah, you mean read the contents of the FLASH address space.

Get the Flash address space to me means determining it's range - which is the question we started with.

I did actually say (and you quoted me as saying) "get at the Flash address space" :slight_smile:

I'm sorry, that's the kind of colloquialism that I normally try to avoid in discussions with an international readership. "Access" would be a good equivalent, hence (as you say) "read" (or in the general case write, verify etc.).

MarkMLl

At least on an Uno with Arduino 1.8.12, asking pgm_read_byte() to look at the bootloader space, including the version information at the top, appears to return 1 for all addresses.

Intel hex of the transition between application and bootloader space looks like

:207DE000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA3
:207E0000112484B714BE81FFF0D085E08093810082E08093C00088E18093C10086E08093FC

Code fragment:

    Serial.println();
    Serial.print(0x7dfe, HEX);
    Serial.print(" ");
    Serial.print(pgm_read_byte(0x7dfe), HEX);

    Serial.println();
    Serial.print(0x7dff, HEX);
    Serial.print(" ");
    Serial.print(pgm_read_byte(0x7dff), HEX);

    Serial.println();
    Serial.print(0x7e00, HEX);
    Serial.print(" ");
    Serial.print(pgm_read_byte(0x7e00), HEX);

    Serial.println();
    Serial.print(0x7e01, HEX);
    Serial.print(" ");
    Serial.print(pgm_read_byte(0x7e01), HEX);

    Serial.println();

Output:

7DFE FF
7DFF FF
7E00 1
7E01 1

Is there a reliable alternative?

MarkMLl

The bootloader files included with the Arduino IDE, and presumably used on the production UNO boards, is version 4.4, and from the boards.txt file it appears the lock bits are set to prevent code from reading the bootloader section.

Thanks David, it took me a moment to digest that :slight_smile:

OK, so presumably that's one reason why AvrDude uses temporary fuse values. That is, as they say, a bit of a bummer since it means that it's not possible to decide at runtime whether the reset reason in r2 is valid: that was introduced in Optiboot 4.6 so doesn't work on the majority of boards in as-shipped condition.

From what I can see reading around, it looks as though to clear the relevant lock bit (set byte to 0x20 to make the loader readable?) I'd need to save the existing firmware using AvrDude (which presumably can see it since it's executing from bootloader space), erase the chip, rewrite the loader via ISP and then write the new lockbits.

MarkMLl

As long as you have an ISP programmer, you can upgrade the bootloader to a more recent version. If you are using an atmega328, MCDdude’s MiniCore has precompiled bootloader files using Optiboot version 7.0, or you can download the Optiboot source and compile the current version yourself.

If you only want to change the fuse setting to allow reading the bootloader section of FLASH, you can edit the fuse setting in the boards.txt file and burn the bootloader from the IDE, still requires an ISP programmer, but already has the default optiboot files.

I'm having to work from source since I had to add some clock speeds etc. to support the board I've been tinkering with over the last few days, and have ended up driving AvrDude manually... in part because when using a serial connection the reset sense is inverted (due to the way the circuit has been designed around the chip).

I checked and on that board the lock bits were being left at 0x3f, which meant that I was able to get at the Optiboot version number... even if I wasn't able to on standard Arduinos. And conveniently, a 10->6 pin adapter arrived yesterday :slight_smile:

MarkMLl