From eb4416a5f04ffb9f3d18f5a5f579cfd18757068a Mon Sep 17 00:00:00 2001 From: WestfW Date: Mon, 28 Sep 2020 02:57:14 -0700 Subject: [PATCH] Add optiboot_x nvmctrl support to optiboot.h minor fixes in test_dospm itself. test_dospm (which uses only the "high level" calls to read and write pages) now works the same (I think) on both traditional AVR and new mega0/xTiny chips. --- optiboot/examples/test_dospm/optiboot.h | 155 +++++++++++++++----- optiboot/examples/test_dospm/test_dospm.ino | 8 +- 2 files changed, 125 insertions(+), 38 deletions(-) diff --git a/optiboot/examples/test_dospm/optiboot.h b/optiboot/examples/test_dospm/optiboot.h index 771783d..1e1759f 100644 --- a/optiboot/examples/test_dospm/optiboot.h +++ b/optiboot/examples/test_dospm/optiboot.h @@ -2,6 +2,8 @@ | | | June 2015 by Marek Wodzinski, https://github.com/majekw | | Modified June 2016 by MCUdude, https://github.com/MCUdude | + | Modified Sep 2020 for mega0/xTiny (optiboot_x) | + | by Bill Westfield https://github.com/westfw | | Released to public domain | | | | This header file gives possibility to use SPM instruction | @@ -24,9 +26,18 @@ #ifndef _OPTIBOOT_H_ #define _OPTIBOOT_H_ 1 +#ifdef SPMCSR +// Mega0 does not have SPMCSR #include +#endif #include "Arduino.h" +// figure out whether we have SPM or NVMCTRL +#ifdef FUSE_BOOTEND +#define USE_NVMCTRL 1 +#define NVMCTRL_CMD_COPY_gc (NVMCTRL_CMD_gm+1) // one beyond existing commands. +#define SPM_PAGESIZE (PROGMEM_PAGE_SIZE) +#endif /* * Main 'magic' function - enter to bootloader do_spm function @@ -42,6 +53,7 @@ // '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 (*do_nvmctrl_t)(uint16_t address, uint8_t command, uint8_t data); /* @@ -56,12 +68,20 @@ typedef void (*do_spm_t)(uint16_t address, uint8_t command, uint16_t data); typedef uint16_t optiboot_addr_t; #endif +#ifdef USE_NVMCTRL +// Mega0/xTiny/etc. Bootloader is in low memory. + const do_spm_t do_nvmctrl = (do_spm_t)((PROGMEM_START+2)>>1); +#else #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 +#endif +#ifndef USE_NVMCTRL + +// SPM-based functions /* * The same as do_spm but with disable/restore interrupts state @@ -81,14 +101,12 @@ void do_spm_cli(optiboot_addr_t address, uint8_t command, uint16_t data) { uint8_t eind = EIND; EIND = FLASHEND / 0x20000; #endif - // do_spm accepts only lower 16 bits of address - do_spm((address & 0xffff), command, data); + do_spm((address & 0xffff), command, data); // do_spm accepts only lower 16 bits of address #ifdef EIND EIND = eind; #endif #else - // 16 bit address - no problems to pass directly - do_spm(address, command, data); + do_spm(address, command, data); // 16 bit address - no problems to pass directly #endif SREG = sreg_save; // restore last interrupts state } @@ -112,44 +130,11 @@ void optiboot_page_write(optiboot_addr_t address) { } - /* * Higher level functions for reading and writing from flash * See the examples for more info on how to use these functions */ -// Function to read a flash page and store it in an array (storage_array[]) -void optiboot_readPage(const uint8_t allocated_flash_space[], - uint8_t storage_array[], uint16_t page, - char blank_character) -{ - uint8_t read_character; - for(uint16_t j = 0; j < SPM_PAGESIZE; j++) - { - read_character = pgm_read_byte(&allocated_flash_space[j + SPM_PAGESIZE*(page-1)]); - if(read_character != 0 && read_character != 255) - storage_array[j] = read_character; - else - storage_array[j] = blank_character; - } -} - - -// Function to read a flash page and store it in an array (storage_array[]), -// but without blank_character -void optiboot_readPage(const uint8_t allocated_flash_space[], - uint8_t storage_array[], uint16_t page) -{ - uint8_t read_character; - for(uint16_t j = 0; j < SPM_PAGESIZE; j++) - { - read_character = pgm_read_byte(&allocated_flash_space[j + SPM_PAGESIZE*(page-1)]); - if(read_character != 0 && read_character != 255) - storage_array[j] = read_character; - } -} - - // Function to write data to a flash page void optiboot_writePage(const uint8_t allocated_flash_space[], uint8_t data_to_store[], uint16_t page) @@ -175,5 +160,101 @@ void optiboot_writePage(const uint8_t allocated_flash_space[], optiboot_page_write((optiboot_addr_t)(void*) &allocated_flash_space[SPM_PAGESIZE*(page-1)]); } +#else // Newer Mega0/xTiny chips with NVMCTRL + +/* + * The same as do_nvmctrl but with disable/restore interrupts state + * required to succesfull execution + * + * Currently, there are no mega0/xTint parts with more than 64k, and when there are + * they'll need extra effort beyond just RAMPZ :-( + */ +void do_nvmctrl_cli(optiboot_addr_t address, uint8_t command, uint16_t data) +{ + uint8_t sreg_save; + + sreg_save = SREG; // save old SREG value + asm volatile("cli"); // disable interrupts + do_nvmctrl(address, command, data); // 16 bit address - no problems to pass directly + SREG = sreg_save; // restore last interrupts state +} + + +// Erase page in FLASH +void optiboot_page_erase(optiboot_addr_t address) +{ + // set page by writing to address. + do_nvmctrl(address+MAPPED_PROGMEM_START, NVMCTRL_CMD_COPY_gc, 0xFF); + do_nvmctrl_cli(0, NVMCTRL_CMD_PAGEERASE_gc, 0); // do actual erase +} + + +// Write word into temporary buffer +void optiboot_page_fill(optiboot_addr_t address, uint16_t data) +{ + do_nvmctrl(address+MAPPED_PROGMEM_START, NVMCTRL_CMD_COPY_gc, data & 0xFF); + do_nvmctrl(address+MAPPED_PROGMEM_START, NVMCTRL_CMD_COPY_gc, data >> 8); +} + + +//Write temporary buffer into FLASH +void optiboot_page_write(optiboot_addr_t address) +{ + do_nvmctrl_cli(address, NVMCTRL_CMD_PAGEWRITE_gc, 0); +} + + +/* + * Higher level functions for reading and writing from flash + * See the examples for more info on how to use these functions + */ + +// Function to write data to a flash page +void optiboot_writePage(const uint8_t allocated_flash_space[], + uint8_t data_to_store[], uint16_t page) +{ + const uint8_t *adjusted_address; + // Copy ram buffer to temporary flash buffer + for(uint16_t i = 0; i < SPM_PAGESIZE; i++) + { + adjusted_address = &allocated_flash_space[i + SPM_PAGESIZE*(page-1)]; + adjusted_address += MAPPED_PROGMEM_START; + do_nvmctrl((optiboot_addr_t)(void*) adjusted_address, + NVMCTRL_CMD_COPY_gc, data_to_store[i]); + } + do_nvmctrl_cli(0, NVMCTRL_CMD_PAGEERASEWRITE_gc, 0); +} + +#endif // USE_NVMTRL + +// Function to read a flash page and store it in an array (storage_array[]) +// (these can be shared between old and new AVRs.) +void optiboot_readPage(const uint8_t allocated_flash_space[], + uint8_t storage_array[], uint16_t page, char blank_character) +{ + uint8_t read_character; + for(uint16_t j = 0; j < SPM_PAGESIZE; j++) + { + read_character = pgm_read_byte(&allocated_flash_space[j + SPM_PAGESIZE*(page-1)]); + if(read_character != 0 && read_character != 255) + storage_array[j] = read_character; + else + storage_array[j] = blank_character; + } +} + + +// Function to read a flash page and store it in an array (storage_array[]), but without blank_character +void optiboot_readPage(const uint8_t allocated_flash_space[], + uint8_t storage_array[], uint16_t page) +{ + uint8_t read_character; + for(uint16_t j = 0; j < SPM_PAGESIZE; j++) + { + read_character = pgm_read_byte(&allocated_flash_space[j + SPM_PAGESIZE*(page-1)]); + if(read_character != 0 && read_character != 255) + storage_array[j] = read_character; + } +} #endif /* _OPTIBOOT_H_ */ diff --git a/optiboot/examples/test_dospm/test_dospm.ino b/optiboot/examples/test_dospm/test_dospm.ino index 32d968b..5077e8a 100644 --- a/optiboot/examples/test_dospm/test_dospm.ino +++ b/optiboot/examples/test_dospm/test_dospm.ino @@ -49,7 +49,7 @@ uint16_t pageNumber; char returnToMenu; // The temporary data (data that's read or is about to get written) is stored here -uint8_t ramBuffer[SPM_PAGESIZE]; +uint8_t ramBuffer[SPM_PAGESIZE+1]; // This array allocates the space you'll be able to write to const uint8_t flashSpace[SPM_PAGESIZE * NUMBER_OF_PAGES] __attribute__ (( aligned(SPM_PAGESIZE) )) PROGMEM = { @@ -71,6 +71,11 @@ void loop() Serial.println(); Serial.println(F("|------------------------------------------------|")); Serial.println(F("| Welcome to the Optiboot flash writer example! |")); +#ifdef USE_NVMCTRL + Serial.println(F("| Running on mega0/xTiny with NVMCTRL support. |")); +#else + Serial.println(F("| Running on traditional AVR with SPM support. |")); +#endif Serial.print(F("| Each flash page is ")); Serial.print(SPM_PAGESIZE); Serial.println(F(" bytes long. |")); @@ -155,6 +160,7 @@ void loop() Serial.print(F("\nContent of page ")); Serial.print(pageNumber); Serial.println(F(":")); + ramBuffer[SPM_PAGESIZE] = 0; // null terminate! Serial.println((char*)ramBuffer); }