mirror of
https://github.com/Optiboot/optiboot.git
synced 2025-08-17 21:41:03 +03:00
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.
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
| |
|
| |
|
||||||
| June 2015 by Marek Wodzinski, https://github.com/majekw |
|
| June 2015 by Marek Wodzinski, https://github.com/majekw |
|
||||||
| Modified June 2016 by MCUdude, https://github.com/MCUdude |
|
| 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 |
|
| Released to public domain |
|
||||||
| |
|
| |
|
||||||
| This header file gives possibility to use SPM instruction |
|
| This header file gives possibility to use SPM instruction |
|
||||||
@@ -24,9 +26,18 @@
|
|||||||
#ifndef _OPTIBOOT_H_
|
#ifndef _OPTIBOOT_H_
|
||||||
#define _OPTIBOOT_H_ 1
|
#define _OPTIBOOT_H_ 1
|
||||||
|
|
||||||
|
#ifdef SPMCSR
|
||||||
|
// Mega0 does not have SPMCSR
|
||||||
#include <avr/boot.h>
|
#include <avr/boot.h>
|
||||||
|
#endif
|
||||||
#include "Arduino.h"
|
#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
|
* Main 'magic' function - enter to bootloader do_spm function
|
||||||
@@ -42,6 +53,7 @@
|
|||||||
// 'typedef' (in following line) and 'const' (few lines below)
|
// 'typedef' (in following line) and 'const' (few lines below)
|
||||||
// are a way to define external function at some arbitrary address
|
// 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_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;
|
typedef uint16_t optiboot_addr_t;
|
||||||
#endif
|
#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
|
#if FLASHEND > 65534
|
||||||
const do_spm_t do_spm = (do_spm_t)((FLASHEND-1023+2)>>1);
|
const do_spm_t do_spm = (do_spm_t)((FLASHEND-1023+2)>>1);
|
||||||
#else
|
#else
|
||||||
const do_spm_t do_spm = (do_spm_t)((FLASHEND-511+2)>>1);
|
const do_spm_t do_spm = (do_spm_t)((FLASHEND-511+2)>>1);
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_NVMCTRL
|
||||||
|
|
||||||
|
// SPM-based functions
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The same as do_spm but with disable/restore interrupts state
|
* 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;
|
uint8_t eind = EIND;
|
||||||
EIND = FLASHEND / 0x20000;
|
EIND = FLASHEND / 0x20000;
|
||||||
#endif
|
#endif
|
||||||
// do_spm accepts only lower 16 bits of address
|
do_spm((address & 0xffff), command, data); // do_spm accepts only lower 16 bits of address
|
||||||
do_spm((address & 0xffff), command, data);
|
|
||||||
#ifdef EIND
|
#ifdef EIND
|
||||||
EIND = eind;
|
EIND = eind;
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
// 16 bit address - no problems to pass directly
|
do_spm(address, command, data); // 16 bit address - no problems to pass directly
|
||||||
do_spm(address, command, data);
|
|
||||||
#endif
|
#endif
|
||||||
SREG = sreg_save; // restore last interrupts state
|
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
|
* Higher level functions for reading and writing from flash
|
||||||
* See the examples for more info on how to use these functions
|
* 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
|
// Function to write data to a flash page
|
||||||
void optiboot_writePage(const uint8_t allocated_flash_space[],
|
void optiboot_writePage(const uint8_t allocated_flash_space[],
|
||||||
uint8_t data_to_store[], uint16_t page)
|
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)]);
|
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_ */
|
#endif /* _OPTIBOOT_H_ */
|
||||||
|
@@ -49,7 +49,7 @@ uint16_t pageNumber;
|
|||||||
char returnToMenu;
|
char returnToMenu;
|
||||||
|
|
||||||
// The temporary data (data that's read or is about to get written) is stored here
|
// 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
|
// 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 = {
|
const uint8_t flashSpace[SPM_PAGESIZE * NUMBER_OF_PAGES] __attribute__ (( aligned(SPM_PAGESIZE) )) PROGMEM = {
|
||||||
@@ -71,6 +71,11 @@ void loop()
|
|||||||
Serial.println();
|
Serial.println();
|
||||||
Serial.println(F("|------------------------------------------------|"));
|
Serial.println(F("|------------------------------------------------|"));
|
||||||
Serial.println(F("| Welcome to the Optiboot flash writer example! |"));
|
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(F("| Each flash page is "));
|
||||||
Serial.print(SPM_PAGESIZE);
|
Serial.print(SPM_PAGESIZE);
|
||||||
Serial.println(F(" bytes long. |"));
|
Serial.println(F(" bytes long. |"));
|
||||||
@@ -155,6 +160,7 @@ void loop()
|
|||||||
Serial.print(F("\nContent of page "));
|
Serial.print(F("\nContent of page "));
|
||||||
Serial.print(pageNumber);
|
Serial.print(pageNumber);
|
||||||
Serial.println(F(":"));
|
Serial.println(F(":"));
|
||||||
|
ramBuffer[SPM_PAGESIZE] = 0; // null terminate!
|
||||||
Serial.println((char*)ramBuffer);
|
Serial.println((char*)ramBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user