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 |
|
||||
| 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 <avr/boot.h>
|
||||
#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_ */
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user