platformio / platform-atmelavr

Atmel AVR: development platform for PlatformIO
https://registry.platformio.org/platforms/platformio/atmelavr
Apache License 2.0
140 stars 105 forks source link

Add support for the Urboot bootloader #321

Closed MCUdude closed 12 months ago

MCUdude commented 1 year ago

See #320 for details.

This PR adds support for the Urboot bootloader and makes it the default choice when using MigntyCore, MiniCore, MegaCore, or MajorCore. If you don't know what Urboot is, please take a look at the README in the Urboot repo.

Optiboot is still available, but not the default option anymore. You can still force Optiboot by utilizing
board_bootloader.type = [urboot, optiboot, no_bootloader]

All these changes will be documented in the PlatformIO.md document in each of the core repos, so users have a ready-made platformio.ini template they can use to get started.

I had to make quite a lot of changes to the build files to get it working. Urboot hex files have different names than the Optiboot flash files, and Urboot is a "vector bootloader", which means that there's no need for a hardware bootloader section. It also has support for EEPROM uploads (not the ATtiny13, but all other target running Urboot).

On chips with 64kB flash or more, the bootloader size has been reduced from 1024 bytes to 512 bytes. On chips with 32kB flash or less, the bootload size has been reduced from 512 bytes to 384 bytes. The bootloader size on the ATtiny13/A is only 256 bytes, and that's why it doesn't have EEPROM support. Note that all bootloaders* support autobaud, which lets the user set whatever baud rate he/she likes without having to re-flash the bootloader.

@valeros and/or @ivankravets please review! I'm more than open to suggestions on how the code could be further improved. As you can probably see, I'm no Python programmer, so pardon my "C accent" 😄

The Urboot bootloader requires Avrdude 7.1 or greater, and my cores are already using Avrdude 7.2, so this shouldn't be a problem.

* The ATtiny13 does not support auto baud due to the limited flash capacity. UART2 and UART3 on the ATmega640/1280/2560 do not support autobaud either, since their registers are placed further up in the io memory (addresses greater than 0xff), requiring extra instructions to read and write to these. PlatformIO will load a "fixed" bootloader in this case, where the clock frequency (f_cpu) and bootloader baud rate have to be known, just like with Optiboot.

MCUdude commented 1 year ago

Thanks for looking at the PR @valeros! Please let me know if you have any questions

valeros commented 1 year ago

Please let me know if you have any questions

Please take a look at my comment above.

MCUdude commented 1 year ago

Thanks @valeros!

I found a tiny bug that prevented the fuses.py script to run for targets without an efuse.

BTW I'm having a hard time getting PlatformIO to account for the bootloader size of the ATtiny13.

Have a look at this. When I run pio run -e Upload_UART, PlatformIO reports that there are 1024 bytes of flash, but since I have specified board_bootloader.type = urboot, it should subtract 256 from that number (code in arduino.py). It works with the other cores, but not MicroCore:

$ pio run -e Upload_UART
Processing Upload_UART (platform: atmelavr; framework: arduino; board: ATtiny13A)
------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/atmelavr/ATtiny13A.html
PLATFORM: Atmel AVR (4.2.0+sha.265921d) > ATtiny13A
HARDWARE: ATTINY13A 9MHz, 64B RAM, 1KB Flash
DEBUG: Current (simavr) External (simavr)
PACKAGES: 
 - framework-arduino-avr-microcore @ 2.3.0 
 - toolchain-atmelavr @ 1.70300.191015 (7.3.0)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 7 compatible libraries
Scanning dependencies...
No dependencies
Building in release mode
Checking size .pio/build/Upload_UART/firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [========= ]  90.6% (used 58 bytes from 64 bytes)
Flash: [===       ]  25.6% (used 262 bytes from 1024 bytes)
======================================= [SUCCESS] Took 0.79 seconds =======================================

Environment    Status    Duration
-------------  --------  ------------
Upload_UART    SUCCESS   00:00:00.793
======================================= 1 succeeded in 00:00:00.793 =======================================
[platformio]
default_envs = Upload_ISP ; Default build target

; Common settings for all environments
[env]
platform = atmelavr
framework = arduino

; TARGET SETTINGS
; Chip in use
board = ATtiny13A
; Clock frequency in [Hz]
board_build.f_cpu = 9600000L

; BUILD OPTIONS
; Extra build flags
build_flags = 
; Uflag existing flags
build_unflags =

; SERIAL MONITOR OPTIONS
; Serial monitor port defined in the Upload_UART environment
monitor_port = ${env:Upload_UART.upload_port}
; Serial monitor baud rate
monitor_speed = 115200

; Run the following command to upload with this environment
; pio run -e Upload_ISP -t upload
[env:Upload_ISP]
upload_protocol = custom              ; Custom upload procedure
upload_flags =                        ; Avrdude upload flags
  -C$PROJECT_PACKAGES_DIR/tool-avrdude/avrdude.conf
  -p$BOARD_MCU
  -PUSB
  -cusbasp
  -Uflash:w:$SOURCE:i
upload_command = avrdude $UPLOAD_FLAGS ; Avrdude upload command

; Run the following command to upload with this environment
; pio run -e Upload_UART -t upload
[env:Upload_UART]
upload_protocol = urclock            ; Serial bootloader protocol
;board_upload.maximum_size = 768      ; Bootloader occupies 256 bytes of flash
upload_port = /dev/cu.usbserial      ; User defined serial upload port
board_upload.speed = ${env:fuses_bootloader.board_bootloader.speed} ; Bootloader baud rate
board_bootloader.type = urboot    

; Run the following command to set fuses with no bootloader
; pio run -e set_fuses -t fuses
[env:set_fuses]
board_hardware.uart = no_bootloader
board_hardware.oscillator = internal ; Oscillator type
board_hardware.bod = 2.7v            ; Set brown-out detection
board_hardware.eesave = yes          ; Preserve EEPROM when uploading using programmer
upload_protocol = usbasp             ; Use the USBasp as programmer
upload_flags =                       ; Divide SPI clock by 8
  -B8

; Run the following command to set fuses and flash a bootloader
; pio run -e fuses -t fuses_bootloader
[env:fuses_bootloader]
extends = env:set_fuses              ; Continue where set_fuses left off
board_hardware.uart = swio_rxb1_txb0 ; Software UART, RXD=PB1, TXD=PB0
board_hardware.f_cpu_error = +1.25   ; Percent internal clock error (-10 to 10 in 1.25 steps)
board_bootloader.speed = 57600       ; Set bootloader baud rate

main.cpp:

#include <Arduino.h>

const uint8_t led_pin = LED_BUILTIN;

void setup()
{
  pinMode(led_pin, OUTPUT);
  Serial.begin(9600);
  Serial.print("Hello program begin!\n");
}

void loop()
{
  digitalWrite(led_pin, HIGH);
  Serial.print("LED is now high!\n");
  delay(1000);
  digitalWrite(led_pin, LOW);
  Serial.print("LED is now low!\n");
  delay(1000);
}
valeros commented 1 year ago

BTW I'm having a hard time getting PlatformIO to account for the bootloader size of the ATtiny13.

Probably because the size adjustment is enabled only for those cores? https://github.com/platformio/platform-atmelavr/blob/develop/builder/frameworks/arduino.py#L137-L140

MCUdude commented 1 year ago

@valeros I think I found a viable solution that makes it easy for PlatformIO to figure out the correct flash size. It also fixes a bug where it would always assume a bootloader was in use if the user didn't explicitly specify that no bootloader is in use. A more reasonable way it to look at which upload protocol the user specifies. This will only affect my cores, and not the official Arduino boards.

I'll do some more testing on all my classic AVR cores, and report back and/or push new commits if I find anything.

🇺🇦 🇺🇦

MCUdude commented 1 year ago

BTW, are there such a thing as upload_unflags? I've added -xnometadata when uploading to a device that's running Urboot to have a predicable amount of flash space, but others may want to experiment with the metadata feature the bootloader (and Avrdude) provides. But then the "built-in" -xnometadata flag needs to be cleared.

valeros commented 1 year ago

BTW, are there such a thing as upload_unflags?

Unfortunately, there is no such thing, but users can always craft their own upload command via the upload_command option.

MCUdude commented 1 year ago

Unfortunately, there is no such thing, but users can always craft their own upload command via the upload_command option.

I understand. If this were to be added sometime in the future, does a PR for this belong to this repo, or the platformio core repo?

valeros commented 1 year ago

I understand. If this were to be added sometime in the future, does a PR for this belong to this repo, or the platformio core repo?

Yes, the PlatformIO Core repository is the right place for this feature request.

MCUdude commented 1 year ago

@valeros I think this PR is ready. Please look though my last commit, and do the appropriate changes. When you've done, I'd like to do a final test for all my cores, just to make sure we are 100% dialed!

valeros commented 1 year ago

Hi @MCUdude, the PR looks fine to me, please let me know when to merge.

valeros commented 12 months ago

Hi @MCUdude, a new release of the atmelavar dev-platform is planned at the end of the month, it would be great if the updated support for Urboot is added.

MCUdude commented 12 months ago

Good! I'll go though it tonight to make sure there are no obvious error in the PR.

MCUdude commented 12 months ago

I just read through the code again, and I think this can be merged without further ado. If someone claims that there is an issue with the fuses/bootloader script, just tag me, and I'll see what I can do.

valeros commented 12 months ago

Many thanks for the PR, merged!