avrdudes / avrdude

AVRDUDE is a utility to program AVR microcontrollers
GNU General Public License v2.0
648 stars 123 forks source link

Multi-memory files #1817

Closed MCUdude closed 1 day ago

MCUdude commented 3 weeks ago

I figured I'd test the USB capabilities on my AVR-DU Curiosity Nano board, so I decided to test the newly released USB CDC to UART example provided by Microchip. The 1.0.0 release contains precompiled hex files for the AVR64DU32, but to my surprise, the file contains data at address 0x820000; outside the flash memory space.

$ avrdude -cdryrun -pavr64du32 -Uflash:w:/mnt/c/Users/Hans/Downloads/hex-files/avr64du32-cnano-usb-cdc-to-usart-bridge-mplab-mcc.X_pro_1.0.0.hex:i
avrdude: AVR device initialized and ready to accept instructions
avrdude: device signature = 0x1e9621 (probably 64du32)
avrdude: Note: flash memory has been specified, an erase cycle will be performed.
         To disable this feature, specify the -D option.
avrdude: erasing chip

avrdude: processing -U flash:w:/mnt/c/Users/Hans/Downloads/hex-files/avr64du32-cnano-usb-cdc-to-usart-bridge-mplab-mcc.X_pro_1.0.0.hex:i
avrdude error: Intel Hex record [0x820000, 0x82000b] out of range [0x0000, 0xffff]
        at line 726 of /mnt/c/Users/Hans/Downloads/hex-files/avr64du32-cnano-usb-cdc-to-usart-bridge-mplab-mcc.X_pro_1.0.0.hex
avrdude error: read from file /mnt/c/Users/Hans/Downloads/hex-files/avr64du32-cnano-usb-cdc-to-usart-bridge-mplab-mcc.X_pro_1.0.0.hex failed

avrdude done.  Thank you.

$ avrdude -cdryrun -pavr64du32 -t
avrdude: AVR device initialized and ready to accept instructions
avrdude: device signature = 0x1e9621 (probably 64du32)

avrdude: processing -t interactive terminal
avrdude> write flash 0 0xffff /mnt/c/Users/Hans/Downloads/hex-files/avr64du32-cnano-usb-cdc-to-usart-bridge-mplab-mcc.X_pro_1.0.0.hex
avrdude error: Intel Hex record [0x820000, 0x82000b] out of range [0x0000, 0xffff]
        at line 726 of /mnt/c/Users/Hans/Downloads/hex-files/avr64du32-cnano-usb-cdc-to-usart-bridge-mplab-mcc.X_pro_1.0.0.hex
avrdude error: (write) data /mnt/c/Users/Hans/Downloads/hex-files/avr64du32-cnano-usb-cdc-to-usart-bridge-mplab-mcc.X_pro_1.0.0.hex: unable to read the Intel Hex file
avrdude>

Would it be a good idea to let the user specify "how much" of the hex file that should be written? in my case, write flash 0 0xffff file.hex? This can be useful if the user wants to flash a pre-compiled hex file that also contains a bootloader, but wants to leave this out.

stefanrueger commented 3 weeks ago

let the user specify "how much" of the hex file that should be written? in my case, write flash 0 0xffff file.hex?

The terminal write already implements the idea of writing a subset of an input file, albeit in fill mode only:

$ avrdude -qqc dryrun -p m328p -T "write -?" 2>&1 | grep len
        write <mem> <addr> <len> <data>[,] {<data>[,]} ... # Fill, see below
Ellipsis ... writes <len> bytes padded by repeating the last <data> item.
Both the <addr> and <len> can be negative numbers; a negative <addr> starts
an interval from that many bytes below the memory size; a negative <len> ends

The ellipsis ... to indicate fill mode is needed: regular write takes the number after the address to be data.

So, you got this almost: it should be write flash 0 0x10000 file.hex ... or write flash 0 -1 file.hex ...

However, this would not help here: the problem at hand is that the specified hex file has records outside the memory address space. AVRDUDE takes a dim view and balks at reading the hex file. This could be made a warning provided -F is used.

mcuee commented 3 weeks ago

In this particular case, the memory region is probably valid but avedude does not recognize it yet.

stefanrueger commented 3 weeks ago

the memory region is probably valid

Hmm, not according to the data sheet: neither MCU data space, nor MCU code space nor UPDI Space extend to 0x820000 avr64duXX

stefanrueger commented 3 weeks ago

Seeing that it's

avrdude error: Intel Hex record [0x820000, 0x82000b]

my guess is that it's a convention on their part to stash the 12 fuse bytes into that region (the remaining 4 fuses are reserved). This is what avr-gcc .elf files do, so further guessing this is a botched conversion from .elf to .hex.

MCUdude commented 3 weeks ago

my guess is that it's a convention on their part to stash the 12 fuse bytes into that region (the remaining 4 fuses are reserved). This is what avr-gcc .elf files do, so further guessing this is a botched conversion from .elf to .hex.

@xedbg any thoughts on this? Is this something MPLAB X / the XC8 compiler does automatically, or can be instructed to do?

xedbg commented 3 weeks ago

Well, all the AVR memory types are "stashed" at magic-token locations in the hexfile (EEPROM is at 0x810000 for example) so this is nothing new. The fact that FUSES are included by default by XC8 is in my opinion an improvement - most applications won't run correctly without the correct fuses. So I would say embrace it :)

But a semi-related word of caution - new AVRs now have PDID in the fuse space, which is a great way to program your AVR for the very last time - it would probably deserve an "are you sure?" safety feature of some sort...

mcuee commented 3 weeks ago

@stefanrueger

Actuallly this issue has been discussed before. Please refer to the dicussion here.

Low High Memories
0x00000000 0x007FFFFF Flash
0x00800000 0x0080FFFF RAM
0x00810000 0x0081FFFF EEPROM (*)
0x00820000 0x0082FFFF Fuses
0x00830000 0x0083FFFF Lock bytes
0x00840000 0x0084FFFF Signature
0x00850000 0x0085FFFF User signatures (*)

(*) Not available in all parts

xedbg commented 3 weeks ago

Oh, and don't forget the "boot row" which starts at 0x860000! (also a *)

stefanrueger commented 3 weeks ago

@xedbg Thanks for clarifying. Yes, we discussed a container format for .hex to be used for backup and restore. One idea was to use the address offsets deployed in .elf for .hex files, too. Above table was looking at .elf files (thanks, @mcuee).

Coding .elf files in that manner is OK: for a start, it's documented and used. And AVRDUDE can extract the various memories from the .elf file.

I had not known that Microchip started using .hex files in that fashion. @xedbg is that documented somewhere? I only find mention of 0x8n0000 addresses in 329 .PIC files (from the atdf packs) with a magicoffset(!) name.

Note that AVRDUDE can only read from/write to one particular, specified memory. So when you want to write all memories from the same .elf file to a chip, one can use a bash brace expansion to create, eg, six -U memory writes like so

  avrdude -p m328p -c usbasp -U{eeprom,flash,{l,h,e}fuse,lock}:w:file.elf

There are several problems using .hex files in that fashion, though:

All of that can be progressed (eg, with a new memory designation all) and documentation and hard work etc.

But this isn't what this issue is about. @MCUdude came across an (undocumented?) new type of .hex file that wouldn't play with -U flash:w:file.hex because of out-of-range data. PR #1818 fixes that by giving the user a way of ignoring these.

MCUdude commented 3 weeks ago

I think we should discuss what to do with hex files containing data for more than one memory. Since the next release probably is going to be v8.0, we are allowed to introduce breaking changes.

stefanrueger commented 2 weeks ago

Grea ideas, @MCUdude. There is a bit of topic drift here, so changing the issue to multi-memory input files

Should we write data starting from address 0 or 0x810000

There is nothing AVR8 could usefully write to at flash or any other address 0x810000: the jmp/call opcodes can only address [0, 0x7FFFFF]; this is the theoretical 8 MiB limit of AVR8 flash space. This is why the .elf table reserves 8 MiB for flash, then reserves 64 kiB each for IO/SRAM, EEPROM, Fuses, lock bits, sigrow, userrow and bootrow. (That is how .elf's multi-memory trick works out with a flat address space. AVRDUDE half-heartedly supports programming flash for one(!) 32-bit part AT32xxx; that has an offset in .hex/.srec/ of way beyond above; this does not get in the way of above .elf address space.)

But AVRDUDE could treat a file automatically as multi-memory if it finds data in [0x800000, 0x86FFFF]:

If an input file does not have data in [0x800000, 0x86FFFF], it would still be considered a single-memory (normal) input file, which as before expects data in [0, size-1]. This makes the distinction automatic. No need for the user to do anything or for AVRDUDE to invent new syntax or options.

We might get empty memory files for non-empty input; for example

Things might break, eg, can no longer rely on AVRDUDE to end reading after size bytes for raw, dec, octal, Roman, ... input

Some neat effects of implementing notion of multi-memory files

MCUdude commented 2 weeks ago

But AVRDUDE could treat a file automatically as multi-memory if it finds data in [0x800000, 0x86FFFF]. ... If an input file does not have data in [0x800000, 0x86FFFF], it would still be considered a single-memory (normal) input file, which as before expects data in [0, size-1]. This makes the distinction automatic. No need for the user to do anything or for AVRDUDE to invent new syntax or options.

Great idea! It would be very convenient to be able to decide which memories in a multi-memory input file to write. In my case where I wanted to write the pre-compiled native USB demo to my AVR64DU32, -Ufuses:w:multi-mem.hex:i -Uflash:w::multi-mem.hex:i would "just work". And a future memory type all sounds useful, but not a deal breaker. Maybe Avrdude could reveal to the user which memories the multi-memory input file contains before writing to memory?

stefanrueger commented 2 weeks ago

future memory type all sounds useful, but not a deal breaker

A new feature such as reading a multi-memory hex file to write single memories is bound to entail a host of desires:

Given that the single most popular feature request for AVRDUDE has been creating backups that can be read back, the time might be ripe to look into this.

Maybe Avrdude could reveal to the user which memories the multi-memory input file contains before writing to memory?

Well the hope is that -c dryrun would be the natural answer:

$ avrdude -qqc dryrun -p m328p -U all:w:backup.hex
avrdude error: signature of ATmega328P does not match file (ATmega328PB)
        use -F to override this check

$ avrdude -c dryrun -p m328pb -U all:w:backup.hex
avrdude: AVR device initialized and ready to accept instructions
avrdude: device signature = 0x1e9516 (probably m328pb)

avrdude: processing -U all:w:backup.hex:i
avrdude: reading 33824 bytes for multiple memories from input file backup.hex
avrdude: 1024 bytes eeprom in 1 section [0, 0x3ff]: 256 pages and 0 pad bytes
         writing 1024 bytes to eeprom ...
         writing | ################################################## | 100% 0.00 s 
         reading | ################################################## | 100% 0.00 s 
         1024 bytes of eeprom verified
avrdude: 32768 bytes flash in 1 section [0, 0x7fff]: 256 pages and 0 pad bytes
         writing 32768 bytes to flash ...
         writing | ################################################## | 100% 0.00 s 
         reading | ################################################## | 100% 0.00 s 
         32768 bytes of flash verified
avrdude: 1 byte lfuse in 1 section [0, 0]
         writing 1 byte to lfuse (0x62), 1 byte written, 1 verified
avrdude: 1 byte hfuse in 1 section [0, 0]
         writing 1 byte to hfuse (0xd9), 1 byte written, 1 verified
avrdude: 1 byte efuse in 1 section [0, 0]
         writing 1 byte to efuse (0xf7), 1 byte written, 1 verified
avrdude: 1 byte lock in 1 section [0, 0]
         writing 1 byte to lock (0xff), 1 byte written, 1 verified

avrdude done.  Thank you.

Notice that the mockup also shows the single-byte fuses that would be written? But even better you could ask for the config:

$  avrdude -qqc dryrun -p m328pb -U all:w:backup.hex -T config
config sut_cksel=intrcosc_8mhz_6ck_14ck_65ms # 34
config ckout=co_disabled # 1
config ckdiv8=by_8 # 0
config bootrst=application # 1
config bootsz=bs_2048w # 0
config eesave=ee_erased # 1
config wdton=wdt_programmable # 1
config spien=isp_enabled # 0
config dwen=dw_off # 1
config rstdisbl=external_reset # 1
config bodlevel=bod_disabled # 7
config cfd=cfd_disabled # 0
config lb=no_lock # 3
config blb0=no_lock_in_app # 3
config blb1=no_lock_in_boot # 3

And this before a single electron touches your board!