Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion optiboot/bootloaders/optiboot/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,6 @@ dummy = FORCE
endif
endif


HELPTEXT += "Option SOFT_UART=1 - use a software (bit-banged) UART\n"
ifdef SOFT_UART
ifneq ($(SOFT_UART), 0)
Expand All @@ -243,6 +242,14 @@ APPSPM_CMD = -DAPP_NOSPM=1
endif
endif

HELPTEXT += "Option COPY_FLASH_PAGES_FNC=1 - add copy_flash_pages function\n"
ifdef COPY_FLASH_PAGES_FNC
ifneq ($(COPY_FLASH_PAGES_FNC), 0)
COPY_FLASH_PAGES_FNC_CMD = -DCOPY_FLASH_PAGES_FNC
dummy = FORCE
endif
endif

HELPTEXT += "Option OSCCAL_VALUE=nnn - set set OSCCAL_VALUE in bootloader\n"
ifdef OSCCAL_VALUE
OSCCAL_VALUE_CMD = -DOSCCAL_VALUE=$(OSCCAL_VALUE)
Expand All @@ -253,6 +260,7 @@ endif
COMMON_OPTIONS = $(BAUD_RATE_CMD) $(LED_START_FLASHES_CMD) $(BIGBOOT_CMD)
COMMON_OPTIONS += $(SOFT_UART_CMD) $(LED_DATA_FLASH_CMD) $(LED_CMD) $(SS_CMD)
COMMON_OPTIONS += $(SUPPORT_EEPROM_CMD) $(LED_START_ON_CMD) $(APPSPM_CMD)
COMMON_OPTIONS += $(COPY_FLASH_PAGES_FNC_CMD) $(VERSION_CMD)
COMMON_OPTIONS += $(OSCCAL_VALUE_CMD) $(VERSION_CMD) $(TIMEOUT_CMD)

#UART is handled separately and only passed for devices with more than one.
Expand Down
60 changes: 59 additions & 1 deletion optiboot/bootloaders/optiboot/optiboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@
/* UART number (0..n) for devices with more than */
/* one hardware uart (644P, 1284P, etc) */
/* */
/* COPY_FLASH_PAGES: */
/* Adds function to copy flash pages. The functiom is */
/* intended to be called by the application. */
/* */
/**********************************************************/

/*
Expand Down Expand Up @@ -568,8 +572,14 @@ void pre_main(void) {
" rjmp 1f\n"
#if APP_NOSPM
" ret\n" // if do_spm isn't include, return without doing anything
#undef COPY_FLASH_PAGES_FNC
#else
" rjmp do_spm\n"
#endif
#ifdef COPY_FLASH_PAGES_FNC
" rjmp copy_flash_pages\n"
#else
" ret\n" // if COPY_FLASH_PAGES_FNC isn't include, return without doing anything
#endif
"1:\n"
);
Expand Down Expand Up @@ -1396,7 +1406,55 @@ static void do_spm(uint16_t address, uint8_t command, uint16_t data) {



#if BIGBOOT
#ifdef COPY_FLASH_PAGES_FNC
/*
* Helper function do_spm_rampz wraps do_spm to handle RAMPZ
* for copy_flash_pages function. It is inlined by the compiler.
*
* On devices with more than 64kB flash, 16 bit address is not enough,
* so there is also RAMPZ used in that case.
*/
void do_spm_rampz(uint32_t address, uint8_t command, uint16_t data) {
#ifdef RAMPZ
RAMPZ = (address >> 16) & 0xff; // address bits 23-16 goes to RAMPZ
do_spm((address & 0xffff), command, data); // do_spm accepts only lower 16 bits of address
#else
do_spm(address, command, data); // 16 bit address - no problems to pass directly
#endif
}

/*
* Function copy_flash_pages uses do_spm() function to copy flash pages.
* It is intended to be called by the application over the 'vector table' in pre_main().
* It uses 32bit addresses for use on devices with more then 64 kB flash memory.
* The destination and source address must be page aligned.
* Additionally parameter reset_mcu activates an (almost) immediate watchdog reset of the MCU after pages are copied.
*
* It was created to copy a new version of the aplication stored in the upper half of the flash memory
* to the beginnig of the flash and then reset the MCU to run the new version.
* It is used by ArduinoOTA libray in InternalStorageAVR over utility/optiboot.h.
*/
void copy_flash_pages(uint32_t dest_page_addr, uint32_t src_page_addr, uint16_t page_count, uint8_t reset_mcu) {
int i, j;
for (i = 0; i < page_count; i++) { // do standard spm steps for every page
do_spm_rampz(dest_page_addr, __BOOT_PAGE_ERASE, 0); // erase page
for (j = 0; j < SPM_PAGESIZE; j += 2) { // fill the bytes for the page
#ifdef RAMPZ // only devices with RAMPZ have pgm_read_word_far()
do_spm_rampz(dest_page_addr + j, __BOOT_PAGE_FILL, pgm_read_word_far(src_page_addr + j));
#else
do_spm(dest_page_addr + j, __BOOT_PAGE_FILL, pgm_read_word(src_page_addr + j));
#endif
}
do_spm_rampz(dest_page_addr, __BOOT_PAGE_WRITE, 0); // write the page
dest_page_addr += SPM_PAGESIZE;
src_page_addr += SPM_PAGESIZE;
}
if (reset_mcu) {
watchdogConfig(WATCHDOG_16MS); // for a reset of the MCU
while (1); // to prevent return to application in the 15MS to reset
}
}
#elif defined(BIGBOOT)
/*
* Optiboot is designed to fit in 512 bytes, with a minimum feature set.
* Some chips have a minimum bootloader size of 1024 bytes, and sometimes
Expand Down
43 changes: 36 additions & 7 deletions optiboot/examples/test_dospm/optiboot.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
| |
| June 2015 by Marek Wodzinski, https://github.com/majekw |
| Modified June 2016 by MCUdude, https://github.com/MCUdude |
| Modified Dec 2018 by Juraj Andrassy, https://github.com/jandrassy |
| Released to public domain |
| |
| This header file gives possibility to use SPM instruction |
Expand All @@ -20,7 +21,6 @@
| bootloader spm function. |
|-------------------------------------------------------------------------*/


#ifndef _OPTIBOOT_H_
#define _OPTIBOOT_H_ 1

Expand All @@ -42,12 +42,13 @@
// 'typedef' (in following line) and 'const' (few lines below)
// are a way to define external function at some arbitrary address
typedef void (*do_spm_t)(uint16_t address, uint8_t command, uint16_t data);
typedef void (*copy_flash_pages_t)(uint32_t dest, uint32_t src, uint16_t page_count, uint8_t reset);


/*
* Devices with more than 64KB of flash:
* - have larger bootloader area (1KB) (they are BIGBOOT targets)
* - have RAMPZ register :-)
* - have RAMPZ register :-)
* - need larger variable to hold address (pgmspace.h uses uint32_t)
*/
#ifdef RAMPZ
Expand All @@ -56,11 +57,12 @@ typedef void (*do_spm_t)(uint16_t address, uint8_t command, uint16_t data);
typedef uint16_t optiboot_addr_t;
#endif

#if FLASHEND > 65534
const do_spm_t do_spm = (do_spm_t)((FLASHEND-1023+2)>>1);
#else
const do_spm_t do_spm = (do_spm_t)((FLASHEND-511+2)>>1);
#endif
#if FLASHEND > 65534
const do_spm_t do_spm = (do_spm_t)((FLASHEND-1023+2)>>1);
const copy_flash_pages_t copy_flash_pages = (copy_flash_pages_t)((FLASHEND-1023+4)>>1);
#else
const do_spm_t do_spm = (do_spm_t)((FLASHEND-511+2)>>1);
#endif


/*
Expand All @@ -69,6 +71,9 @@ typedef void (*do_spm_t)(uint16_t address, uint8_t command, uint16_t data);
*
* On devices with more than 64kB flash, 16 bit address is not enough,
* so there is also RAMPZ used in that case.
*
* On devices with more than 128kB flash, 16 bit word address is not enough
* for a function call above 0x20000, so there is also EIND used in that case.
*/
void do_spm_cli(optiboot_addr_t address, uint8_t command, uint16_t data) {
uint8_t sreg_save;
Expand All @@ -93,6 +98,30 @@ void do_spm_cli(optiboot_addr_t address, uint8_t command, uint16_t data) {
SREG = sreg_save; // restore last interrupts state
}

/*
* Copy contents of the flash pages. Addresses must be aligned to page boundary.
*
* On devices with more than 128kB flash, 16 bit word address is not enough
* for a function call above 0x20000, so there is also EIND used in that case.
*
* If reset_mcu is true, watchdog is used to reset the MCU after pages are copied.
* That enables to copy a new version of application from upper half of the flash.
*/
#if FLASHEND > 65534
void copy_flash_pages_cli(uint32_t dest, uint32_t src, uint16_t page_count, uint8_t reset_mcu) {
uint8_t sreg_save = SREG; // save old SREG value
asm volatile("cli"); // disable interrupts
#ifdef EIND
uint8_t eind = EIND;
EIND = FLASHEND / 0x20000;
#endif
copy_flash_pages(dest, src, page_count, reset_mcu);
#ifdef EIND
EIND = eind;
#endif
SREG = sreg_save; // restore last interrupts state
}
#endif

// Erase page in FLASH
void optiboot_page_erase(optiboot_addr_t address) {
Expand Down