Added example MS/TP port to ATxmega XPLAINED A3BU evaluation board.
This commit is contained in:
@@ -0,0 +1,732 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief Non Volatile Memory controller driver
|
||||
*
|
||||
* Copyright (C) 2010-2012 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
||||
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
#include "compiler.h"
|
||||
#include "ccp.h"
|
||||
#include "nvm.h"
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* \weakgroup nvm_signature_group
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Read the device serial
|
||||
*
|
||||
* This function returns the device serial stored in the device.
|
||||
*
|
||||
* \note This function is modifying the NVM.CMD register.
|
||||
* If the application are using program space access in interrupts
|
||||
* (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts
|
||||
* needs to be disabled when running EEPROM access functions. If not
|
||||
* the program space reads will be corrupted.
|
||||
*
|
||||
* \retval storage Pointer to the structure where to store the device serial
|
||||
*/
|
||||
void nvm_read_device_serial(struct nvm_device_serial *storage)
|
||||
{
|
||||
storage->lotnum0 =
|
||||
nvm_read_production_signature_row
|
||||
(nvm_get_production_signature_row_offset(LOTNUM0));
|
||||
storage->lotnum1 =
|
||||
nvm_read_production_signature_row
|
||||
(nvm_get_production_signature_row_offset(LOTNUM1));
|
||||
storage->lotnum2 =
|
||||
nvm_read_production_signature_row
|
||||
(nvm_get_production_signature_row_offset(LOTNUM2));
|
||||
storage->lotnum3 =
|
||||
nvm_read_production_signature_row
|
||||
(nvm_get_production_signature_row_offset(LOTNUM3));
|
||||
storage->lotnum4 =
|
||||
nvm_read_production_signature_row
|
||||
(nvm_get_production_signature_row_offset(LOTNUM4));
|
||||
storage->lotnum5 =
|
||||
nvm_read_production_signature_row
|
||||
(nvm_get_production_signature_row_offset(LOTNUM5));
|
||||
|
||||
storage->wafnum =
|
||||
nvm_read_production_signature_row
|
||||
(nvm_get_production_signature_row_offset(WAFNUM));
|
||||
|
||||
storage->coordx0 =
|
||||
nvm_read_production_signature_row
|
||||
(nvm_get_production_signature_row_offset(COORDX0));
|
||||
storage->coordx1 =
|
||||
nvm_read_production_signature_row
|
||||
(nvm_get_production_signature_row_offset(COORDX1));
|
||||
storage->coordy0 =
|
||||
nvm_read_production_signature_row
|
||||
(nvm_get_production_signature_row_offset(COORDY0));
|
||||
storage->coordy1 =
|
||||
nvm_read_production_signature_row
|
||||
(nvm_get_production_signature_row_offset(COORDY1));
|
||||
}
|
||||
|
||||
//! @}
|
||||
|
||||
/**
|
||||
* \weakgroup nvm_eeprom_group
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Read one byte from EEPROM using mapped access.
|
||||
*
|
||||
* This function reads one byte from EEPROM using mapped access.
|
||||
*
|
||||
* \param addr EEPROM address, between 0 and EEPROM_SIZE
|
||||
*
|
||||
* \return Byte value read from EEPROM.
|
||||
*/
|
||||
uint8_t nvm_eeprom_read_byte(eeprom_addr_t addr)
|
||||
{
|
||||
uint8_t data;
|
||||
Assert(addr <= EEPROM_SIZE);
|
||||
|
||||
/* Wait until NVM is ready */
|
||||
nvm_wait_until_ready();
|
||||
eeprom_enable_mapping();
|
||||
data = *(uint8_t *) (addr + MAPPED_EEPROM_START), eeprom_disable_mapping();
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read buffer within the eeprom
|
||||
*
|
||||
* \param address the address to where to read
|
||||
* \param buf pointer to the data
|
||||
* \param len the number of bytes to read
|
||||
*/
|
||||
void nvm_eeprom_read_buffer(eeprom_addr_t address,
|
||||
void *buf,
|
||||
uint16_t len)
|
||||
{
|
||||
nvm_wait_until_ready();
|
||||
eeprom_enable_mapping();
|
||||
memcpy(buf, (void *) (address + MAPPED_EEPROM_START), len);
|
||||
eeprom_disable_mapping();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Write one byte to EEPROM using IO mapping.
|
||||
*
|
||||
* This function writes one byte to EEPROM using IO-mapped access.
|
||||
* This function will cancel all ongoing EEPROM page buffer loading
|
||||
* operations, if any.
|
||||
*
|
||||
* \param address EEPROM address (max EEPROM_SIZE)
|
||||
* \param value Byte value to write to EEPROM.
|
||||
*/
|
||||
void nvm_eeprom_write_byte(eeprom_addr_t address,
|
||||
uint8_t value)
|
||||
{
|
||||
uint8_t old_cmd;
|
||||
|
||||
Assert(address <= EEPROM_SIZE);
|
||||
/* Flush buffer to make sure no unintentional data is written and load
|
||||
* the "Page Load" command into the command register.
|
||||
*/
|
||||
old_cmd = NVM.CMD;
|
||||
nvm_eeprom_flush_buffer();
|
||||
// Wait until NVM is ready
|
||||
nvm_wait_until_ready();
|
||||
nvm_eeprom_load_byte_to_buffer(address, value);
|
||||
|
||||
// Set address to write to
|
||||
NVM.ADDR2 = 0x00;
|
||||
NVM.ADDR1 = (address >> 8) & 0xFF;
|
||||
NVM.ADDR0 = address & 0xFF;
|
||||
|
||||
/* Issue EEPROM Atomic Write (Erase&Write) command. Load command, write
|
||||
* the protection signature and execute command.
|
||||
*/
|
||||
NVM.CMD = NVM_CMD_ERASE_WRITE_EEPROM_PAGE_gc;
|
||||
nvm_exec();
|
||||
NVM.CMD = old_cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Write buffer within the eeprom
|
||||
*
|
||||
* \param address the address to where to write
|
||||
* \param buf pointer to the data
|
||||
* \param len the number of bytes to write
|
||||
*/
|
||||
void nvm_eeprom_erase_and_write_buffer(eeprom_addr_t address,
|
||||
const void *buf,
|
||||
uint16_t len)
|
||||
{
|
||||
while (len) {
|
||||
if (((address % EEPROM_PAGE_SIZE) == 0) && (len >= EEPROM_PAGE_SIZE)) {
|
||||
// A full page can be written
|
||||
nvm_eeprom_load_page_to_buffer((uint8_t *) buf);
|
||||
nvm_eeprom_atomic_write_page(address / EEPROM_PAGE_SIZE);
|
||||
address += EEPROM_PAGE_SIZE;
|
||||
buf = (uint8_t *) buf + EEPROM_PAGE_SIZE;
|
||||
len -= EEPROM_PAGE_SIZE;
|
||||
} else {
|
||||
nvm_eeprom_write_byte(address++, *(uint8_t *) buf);
|
||||
buf = (uint8_t *) buf + 1;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Flush temporary EEPROM page buffer.
|
||||
*
|
||||
* This function flushes the EEPROM page buffers. This function will cancel
|
||||
* any ongoing EEPROM page buffer loading operations, if any.
|
||||
* This function also works for memory mapped EEPROM access.
|
||||
*
|
||||
* \note An EEPROM write operations will automatically flush the buffer for you.
|
||||
* \note The function does not preserve the value of the NVM.CMD register
|
||||
*/
|
||||
void nvm_eeprom_flush_buffer(void)
|
||||
{
|
||||
// Wait until NVM is ready
|
||||
nvm_wait_until_ready();
|
||||
|
||||
// Flush EEPROM page buffer if necessary
|
||||
if ((NVM.STATUS & NVM_EELOAD_bm) != 0) {
|
||||
NVM.CMD = NVM_CMD_ERASE_EEPROM_BUFFER_gc;
|
||||
nvm_exec();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Load single byte into temporary page buffer.
|
||||
*
|
||||
* This function loads one byte into the temporary EEPROM page buffers.
|
||||
* If memory mapped EEPROM is enabled, this function will not work.
|
||||
* Make sure that the buffer is flushed before starting to load bytes.
|
||||
* Also, if multiple bytes are loaded into the same location, they will
|
||||
* be ANDed together, thus 0x55 and 0xAA will result in 0x00 in the buffer.
|
||||
*
|
||||
* \note Only one page buffer exist, thus only one page can be loaded with
|
||||
* data and programmed into one page. If data needs to be written to
|
||||
* different pages, the loading and writing needs to be repeated.
|
||||
*
|
||||
* \param byte_addr EEPROM Byte address, between 0 and EEPROM_PAGE_SIZE.
|
||||
* \param value Byte value to write to buffer.
|
||||
*/
|
||||
void nvm_eeprom_load_byte_to_buffer(uint8_t byte_addr,
|
||||
uint8_t value)
|
||||
{
|
||||
// Wait until NVM is ready
|
||||
nvm_wait_until_ready();
|
||||
|
||||
eeprom_enable_mapping();
|
||||
*(uint8_t *) (byte_addr + MAPPED_EEPROM_START) = value;
|
||||
eeprom_disable_mapping();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Load entire page into temporary EEPROM page buffer.
|
||||
*
|
||||
* This function loads an entire EEPROM page from an SRAM buffer to
|
||||
* the EEPROM page buffers. If memory mapped EEPROM is enabled, this
|
||||
* function will not work. Make sure that the buffer is flushed before
|
||||
* starting to load bytes.
|
||||
*
|
||||
* \note Only the lower part of the address is used to address the buffer.
|
||||
* Therefore, no address parameter is needed. In the end, the data
|
||||
* is written to the EEPROM page given by the address parameter to the
|
||||
* EEPROM write page operation.
|
||||
*
|
||||
* \param values Pointer to SRAM buffer containing an entire page.
|
||||
*/
|
||||
void nvm_eeprom_load_page_to_buffer(const uint8_t * values)
|
||||
{
|
||||
// Wait until NVM is ready
|
||||
nvm_wait_until_ready();
|
||||
|
||||
// Load multiple bytes into page buffer
|
||||
uint8_t i;
|
||||
for (i = 0; i < EEPROM_PAGE_SIZE; ++i) {
|
||||
nvm_eeprom_load_byte_to_buffer(i, *values);
|
||||
++values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Erase and write bytes from page buffer into EEPROM.
|
||||
*
|
||||
* This function writes the contents of an already loaded EEPROM page
|
||||
* buffer into EEPROM memory.
|
||||
*
|
||||
* As this is an atomic write, the page in EEPROM will be erased
|
||||
* automatically before writing. Note that only the page buffer locations
|
||||
* that have been loaded will be used when writing to EEPROM. Page buffer
|
||||
* locations that have not been loaded will be left untouched in EEPROM.
|
||||
*
|
||||
* \param page_addr EEPROM Page address, between 0 and EEPROM_SIZE/EEPROM_PAGE_SIZE
|
||||
*/
|
||||
void nvm_eeprom_atomic_write_page(uint8_t page_addr)
|
||||
{
|
||||
// Wait until NVM is ready
|
||||
nvm_wait_until_ready();
|
||||
|
||||
// Calculate page address
|
||||
uint16_t address = (uint16_t) (page_addr * EEPROM_PAGE_SIZE);
|
||||
|
||||
Assert(address <= EEPROM_SIZE);
|
||||
|
||||
// Set address
|
||||
NVM.ADDR2 = 0x00;
|
||||
NVM.ADDR1 = (address >> 8) & 0xFF;
|
||||
NVM.ADDR0 = address & 0xFF;
|
||||
|
||||
// Issue EEPROM Atomic Write (Erase&Write) command
|
||||
nvm_issue_command(NVM_CMD_ERASE_WRITE_EEPROM_PAGE_gc);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Write (without erasing) EEPROM page.
|
||||
*
|
||||
* This function writes the contents of an already loaded EEPROM page
|
||||
* buffer into EEPROM memory.
|
||||
*
|
||||
* As this is a split write, the page in EEPROM will _not_ be erased
|
||||
* before writing.
|
||||
*
|
||||
* \param page_addr EEPROM Page address, between 0 and EEPROM_SIZE/EEPROM_PAGE_SIZE
|
||||
*/
|
||||
void nvm_eeprom_split_write_page(uint8_t page_addr)
|
||||
{
|
||||
// Wait until NVM is ready
|
||||
nvm_wait_until_ready();
|
||||
|
||||
// Calculate page address
|
||||
uint16_t address = (uint16_t) (page_addr * EEPROM_PAGE_SIZE);
|
||||
|
||||
Assert(address <= EEPROM_SIZE);
|
||||
|
||||
// Set address
|
||||
NVM.ADDR2 = 0x00;
|
||||
NVM.ADDR1 = (address >> 8) & 0xFF;
|
||||
NVM.ADDR0 = address & 0xFF;
|
||||
|
||||
// Issue EEPROM Split Write command
|
||||
nvm_issue_command(NVM_CMD_WRITE_EEPROM_PAGE_gc);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Fill temporary EEPROM page buffer with value.
|
||||
*
|
||||
* This fills the the EEPROM page buffers with a given value.
|
||||
* If memory mapped EEPROM is enabled, this function will not work.
|
||||
*
|
||||
* \note Only the lower part of the address is used to address the buffer.
|
||||
* Therefore, no address parameter is needed. In the end, the data
|
||||
* is written to the EEPROM page given by the address parameter to the
|
||||
* EEPROM write page operation.
|
||||
*
|
||||
* \param value Value to copy to the page buffer.
|
||||
*/
|
||||
void nvm_eeprom_fill_buffer_with_value(uint8_t value)
|
||||
{
|
||||
nvm_eeprom_flush_buffer();
|
||||
// Wait until NVM is ready
|
||||
nvm_wait_until_ready();
|
||||
// Load multiple bytes into page buffer
|
||||
uint8_t i;
|
||||
for (i = 0; i < EEPROM_PAGE_SIZE; ++i) {
|
||||
nvm_eeprom_load_byte_to_buffer(i, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Erase bytes from EEPROM page.
|
||||
*
|
||||
* This function erases bytes from one EEPROM page, so that every location
|
||||
* written to in the page buffer reads 0xFF.
|
||||
*
|
||||
* \param page_addr EEPROM Page address, between 0 and EEPROM_SIZE/EEPROM_PAGE_SIZE
|
||||
*/
|
||||
void nvm_eeprom_erase_bytes_in_page(uint8_t page_addr)
|
||||
{
|
||||
// Wait until NVM is ready
|
||||
nvm_wait_until_ready();
|
||||
|
||||
// Calculate page address
|
||||
uint16_t address = (uint16_t) (page_addr * EEPROM_PAGE_SIZE);
|
||||
|
||||
Assert(address <= EEPROM_SIZE);
|
||||
|
||||
// Set address
|
||||
NVM.ADDR2 = 0x00;
|
||||
NVM.ADDR1 = (address >> 8) & 0xFF;
|
||||
NVM.ADDR0 = address & 0xFF;
|
||||
|
||||
// Issue EEPROM Erase command
|
||||
nvm_issue_command(NVM_CMD_ERASE_EEPROM_PAGE_gc);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Erase EEPROM page.
|
||||
*
|
||||
* This function erases one EEPROM page, so that every location reads 0xFF.
|
||||
*
|
||||
* \param page_addr EEPROM Page address, between 0 and EEPROM_SIZE/EEPROM_PAGE_SIZE
|
||||
*/
|
||||
void nvm_eeprom_erase_page(uint8_t page_addr)
|
||||
{
|
||||
// Mark all addresses to be deleted
|
||||
nvm_eeprom_fill_buffer_with_value(0xff);
|
||||
// Erase bytes
|
||||
nvm_eeprom_erase_bytes_in_page(page_addr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Erase bytes from all EEPROM pages.
|
||||
*
|
||||
* This function erases bytes from all EEPROM pages, so that every location
|
||||
* written to in the page buffer reads 0xFF.
|
||||
*/
|
||||
void nvm_eeprom_erase_bytes_in_all_pages(void)
|
||||
{
|
||||
// Wait until NVM is ready
|
||||
nvm_wait_until_ready();
|
||||
|
||||
// Issue EEPROM Erase All command
|
||||
nvm_issue_command(NVM_CMD_ERASE_EEPROM_gc);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Erase entire EEPROM memory.
|
||||
*
|
||||
* This function erases the entire EEPROM memory block to 0xFF.
|
||||
*/
|
||||
void nvm_eeprom_erase_all(void)
|
||||
{
|
||||
// Mark all addresses to be deleted
|
||||
nvm_eeprom_fill_buffer_with_value(0xff);
|
||||
// Erase all pages
|
||||
nvm_eeprom_erase_bytes_in_all_pages();
|
||||
}
|
||||
|
||||
//! @}
|
||||
|
||||
|
||||
//! @}
|
||||
|
||||
|
||||
/**
|
||||
* \weakgroup nvm_flash_group
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Issue flash range CRC command
|
||||
*
|
||||
* This function sets the FLASH range CRC command in the NVM.CMD register.
|
||||
* It then loads the start and end byte address of the part of FLASH to
|
||||
* generate a CRC-32 for into the ADDR and DATA registers and finally performs
|
||||
* the execute command.
|
||||
*
|
||||
* \note Should only be called from the CRC module. The function saves and
|
||||
* restores the NVM.CMD register, but if this
|
||||
* function is called from an interrupt, interrupts must be disabled
|
||||
* before this function is called.
|
||||
*
|
||||
* \param start_addr end byte address
|
||||
* \param end_addr start byte address
|
||||
*/
|
||||
void nvm_issue_flash_range_crc(flash_addr_t start_addr,
|
||||
flash_addr_t end_addr)
|
||||
{
|
||||
uint8_t old_cmd;
|
||||
// Save current nvm command
|
||||
old_cmd = NVM.CMD;
|
||||
|
||||
// Load the NVM CMD register with the Flash Range CRC command
|
||||
NVM.CMD = NVM_CMD_FLASH_RANGE_CRC_gc;
|
||||
|
||||
// Load the start byte address in the NVM Address Register
|
||||
NVM.ADDR0 = start_addr & 0xFF;
|
||||
NVM.ADDR1 = (start_addr >> 8) & 0xFF;
|
||||
#if (FLASH_SIZE >= 0x10000UL)
|
||||
NVM.ADDR2 = (start_addr >> 16) & 0xFF;
|
||||
#endif
|
||||
|
||||
// Load the end byte address in NVM Data Register
|
||||
NVM.DATA0 = end_addr & 0xFF;
|
||||
NVM.DATA1 = (end_addr >> 8) & 0xFF;
|
||||
#if (FLASH_SIZE >= 0x10000UL)
|
||||
NVM.DATA2 = (end_addr >> 16) & 0xFF;
|
||||
#endif
|
||||
|
||||
// Execute command
|
||||
ccp_write_io((uint8_t *) & NVM.CTRLA, NVM_CMDEX_bm);
|
||||
|
||||
// Restore command register
|
||||
NVM.CMD = old_cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read buffer within the application section
|
||||
*
|
||||
* \param address the address to where to read
|
||||
* \param buf pointer to the data
|
||||
* \param len the number of bytes to read
|
||||
*/
|
||||
void nvm_flash_read_buffer(flash_addr_t address,
|
||||
void *buf,
|
||||
uint16_t len)
|
||||
{
|
||||
#if (FLASH_SIZE>0x10000)
|
||||
uint32_t opt_address = address;
|
||||
#else
|
||||
uint16_t opt_address = (uint16_t) address;
|
||||
#endif
|
||||
nvm_wait_until_ready();
|
||||
while (len) {
|
||||
*(uint8_t *) buf = nvm_flash_read_byte(opt_address);
|
||||
buf = (uint8_t *) buf + 1;
|
||||
opt_address++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read buffer within the user section
|
||||
*
|
||||
* \param address the address to where to read
|
||||
* \param buf pointer to the data
|
||||
* \param len the number of bytes to read
|
||||
*/
|
||||
void nvm_user_sig_read_buffer(flash_addr_t address,
|
||||
void *buf,
|
||||
uint16_t len)
|
||||
{
|
||||
uint16_t opt_address = (uint16_t) address & (FLASH_PAGE_SIZE - 1);
|
||||
while (len) {
|
||||
*(uint8_t *) buf = nvm_read_user_signature_row(opt_address);
|
||||
buf = (uint8_t *) buf + 1;
|
||||
opt_address++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Write specific parts of user flash section
|
||||
*
|
||||
* \param address the address to where to write
|
||||
* \param buf pointer to the data
|
||||
* \param len the number of bytes to write
|
||||
* \param b_blank_check if True then the page flash is checked before write
|
||||
* to run or not the erase page command.
|
||||
*
|
||||
* Set b_blank_check to false if all application flash is erased before.
|
||||
*/
|
||||
void nvm_user_sig_write_buffer(flash_addr_t address,
|
||||
const void *buf,
|
||||
uint16_t len,
|
||||
bool b_blank_check)
|
||||
{
|
||||
uint16_t w_value;
|
||||
uint16_t page_pos;
|
||||
uint16_t opt_address = (uint16_t) address;
|
||||
bool b_flag_erase = false;
|
||||
|
||||
while (len) {
|
||||
for (page_pos = 0; page_pos < FLASH_PAGE_SIZE; page_pos += 2) {
|
||||
if (b_blank_check) {
|
||||
// Read flash to know if the erase command is mandatory
|
||||
LSB(w_value) = nvm_read_user_signature_row(page_pos);
|
||||
MSB(w_value) = nvm_read_user_signature_row(page_pos + 1);
|
||||
if (w_value != 0xFFFF) {
|
||||
b_flag_erase = true; // The page is not empty
|
||||
}
|
||||
} else {
|
||||
w_value = 0xFFFF;
|
||||
}
|
||||
// Update flash buffer
|
||||
if (len) {
|
||||
if (opt_address == page_pos) {
|
||||
// The MSB of flash word must be changed
|
||||
// because the address is even
|
||||
len--;
|
||||
opt_address++;
|
||||
LSB(w_value) = *(uint8_t *) buf;
|
||||
buf = (uint8_t *) buf + 1;
|
||||
}
|
||||
}
|
||||
if (len) {
|
||||
if (opt_address == (page_pos + 1)) {
|
||||
// The LSB of flash word must be changed
|
||||
// because the user buffer is not empty
|
||||
len--;
|
||||
opt_address++;
|
||||
MSB(w_value) = *(uint8_t *) buf;
|
||||
buf = (uint8_t *) buf + 1;
|
||||
}
|
||||
}
|
||||
// Load flash buffer
|
||||
nvm_flash_load_word_to_buffer(page_pos, w_value);
|
||||
}
|
||||
}
|
||||
// Write flash buffer
|
||||
if (b_flag_erase) {
|
||||
nvm_flash_erase_user_section();
|
||||
}
|
||||
nvm_flash_write_user_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Erase and write specific parts of application flash section
|
||||
*
|
||||
* \param address the address to where to write
|
||||
* \param buf pointer to the data
|
||||
* \param len the number of bytes to write
|
||||
* \param b_blank_check if True then the page flash is checked before write
|
||||
* to run or not the erase page command.
|
||||
*
|
||||
* Set b_blank_check to false if all application flash is erased before.
|
||||
*/
|
||||
void nvm_flash_erase_and_write_buffer(flash_addr_t address,
|
||||
const void *buf,
|
||||
uint16_t len,
|
||||
bool b_blank_check)
|
||||
{
|
||||
uint16_t w_value;
|
||||
uint16_t page_pos;
|
||||
bool b_flag_erase;
|
||||
#if (FLASH_SIZE>0x10000)
|
||||
uint32_t page_address;
|
||||
uint32_t opt_address = address;
|
||||
#else
|
||||
uint16_t page_address;
|
||||
uint16_t opt_address = (uint16_t) address;
|
||||
#endif
|
||||
|
||||
// Compute the start of the page to be modified
|
||||
page_address = opt_address - (opt_address % FLASH_PAGE_SIZE);
|
||||
|
||||
// For each page
|
||||
while (len) {
|
||||
b_flag_erase = false;
|
||||
|
||||
nvm_wait_until_ready();
|
||||
for (page_pos = 0; page_pos < FLASH_PAGE_SIZE; page_pos += 2) {
|
||||
if (b_blank_check) {
|
||||
// Read flash to know if the erase command is mandatory
|
||||
w_value = nvm_flash_read_word(page_address);
|
||||
if (w_value != 0xFFFF) {
|
||||
b_flag_erase = true; // The page is not empty
|
||||
}
|
||||
} else {
|
||||
w_value = 0xFFFF;
|
||||
}
|
||||
|
||||
// Update flash buffer
|
||||
if (len) {
|
||||
if (opt_address == page_address) {
|
||||
// The MSB of flash word must be changed
|
||||
// because the address is even
|
||||
len--;
|
||||
opt_address++;
|
||||
LSB(w_value) = *(uint8_t *) buf;
|
||||
buf = (uint8_t *) buf + 1;
|
||||
}
|
||||
}
|
||||
if (len) {
|
||||
if (opt_address == (page_address + 1)) {
|
||||
// The LSB of flash word must be changed
|
||||
// because the user buffer is not empty
|
||||
len--;
|
||||
opt_address++;
|
||||
MSB(w_value) = *(uint8_t *) buf;
|
||||
buf = (uint8_t *) buf + 1;
|
||||
}
|
||||
}
|
||||
// Load flash buffer
|
||||
nvm_flash_load_word_to_buffer(page_address, w_value);
|
||||
page_address += 2;
|
||||
}
|
||||
|
||||
// Write flash buffer
|
||||
if (b_flag_erase) {
|
||||
nvm_flash_atomic_write_app_page(page_address - FLASH_PAGE_SIZE);
|
||||
} else {
|
||||
nvm_flash_split_write_app_page(page_address - FLASH_PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! @}
|
||||
|
||||
/**
|
||||
* \weakgroup nvm_fuse_lock_group
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Read a fuse byte.
|
||||
*
|
||||
* This function reads and returns the value of a given fuse byte.
|
||||
*
|
||||
* \param fuse Fuse byte to read.
|
||||
*
|
||||
* \return Byte value of fuse.
|
||||
*/
|
||||
uint8_t nvm_fuses_read(enum fuse_byte_t fuse)
|
||||
{
|
||||
// Wait until NVM is ready
|
||||
nvm_wait_until_ready();
|
||||
|
||||
// Set address
|
||||
NVM.ADDR0 = fuse;
|
||||
|
||||
// Issue READ_FUSES command
|
||||
nvm_issue_command(NVM_CMD_READ_FUSES_gc);
|
||||
|
||||
return NVM.DATA0;
|
||||
}
|
||||
|
||||
//! @}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,197 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief Non Volatile Memory controller driver
|
||||
*
|
||||
* Copyright (c) 2010 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
||||
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
#include <assembler.h>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
//! Value to write to CCP for access to protected IO registers.
|
||||
# define CCP_SPM_gc 0x9D
|
||||
|
||||
//! NVM busy flag
|
||||
# define NVM_NVMBUSY_bp 7
|
||||
|
||||
//! NVM command for loading flash buffer
|
||||
# define NVM_CMD_LOAD_FLASH_BUFFER_gc 0x23
|
||||
#elif defined(__IAR_SYSTEMS_ASM__)
|
||||
// All values are defined for IAR
|
||||
#else
|
||||
# error Unknown assembler
|
||||
#endif
|
||||
|
||||
#ifndef __DOXYGEN__
|
||||
PUBLIC_FUNCTION(nvm_read_byte)
|
||||
#if defined(__GNUC__)
|
||||
lds r20, NVM_CMD ; Store NVM command register
|
||||
mov ZL, r22 ; Load byte index into low byte of Z.
|
||||
mov ZH, r23 ; Load high byte into Z.
|
||||
sts NVM_CMD, r24 ; Load prepared command into NVM Command register.
|
||||
lpm r24, Z ; Perform an LPM to read out byte
|
||||
sts NVM_CMD, r20 ; Restore NVM command register
|
||||
#elif defined(__IAR_SYSTEMS_ASM__)
|
||||
lds r20, NVM_CMD ; Store NVM command register
|
||||
mov ZL, r18 ; Load byte index into low byte of Z.
|
||||
mov ZH, r19 ; Load high byte into Z.
|
||||
sts NVM_CMD, r16 ; Load prepared command into NVM Command register.
|
||||
lpm r16, Z ; Perform an LPM to read out byte
|
||||
sts NVM_CMD, r20 ; Restore NVM command register
|
||||
#endif
|
||||
|
||||
ret
|
||||
|
||||
END_FUNC(nvm_read_byte)
|
||||
|
||||
// IAR forgets about include files after each module, so need to include again
|
||||
#if defined(__IAR_SYSTEMS_ASM__)
|
||||
# include <ioavr.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Perform SPM command
|
||||
*/
|
||||
PUBLIC_FUNCTION_SEGMENT(nvm_common_spm, BOOT)
|
||||
|
||||
#if defined(__GNUC__)
|
||||
/**
|
||||
* For GCC:
|
||||
* \param address uint32_t r22:r25
|
||||
* \param nvm_cmd uint8_t r20
|
||||
*/
|
||||
in r25, RAMPZ ; Store RAMPZ. Highest address byte is ignored, so using that
|
||||
out RAMPZ, r24 ; Load R24 into RAMPZ
|
||||
movw ZL, r22 ; Load R22:R23 into Z.
|
||||
lds r24, NVM_CMD ; Store NVM command register (r24 is no longer needed)
|
||||
sts NVM_CMD, r20 ; Load prepared command into NVM Command register.
|
||||
ldi r23, CCP_SPM_gc ; Prepare Protect SPM signature (r23 is no longer needed)
|
||||
sts CCP, r23 ; Enable SPM operation (this disables interrupts for 4 cycles).
|
||||
spm ; Self-program.
|
||||
sts NVM_CMD, r24 ; Restore NVM command register
|
||||
out RAMPZ, r25 ; Restore RAMPZ register.
|
||||
#elif defined(__IAR_SYSTEMS_ASM__)
|
||||
/**
|
||||
* For IAR:
|
||||
* \param address uint32_t r16:r19
|
||||
* \param nvm_cmd uint8_t r20
|
||||
*/
|
||||
in r19, RAMPZ ; Store RAMPZ. Highest address byte is ignored, so using that
|
||||
out RAMPZ, r18 ; Load R18 into RAMPZ
|
||||
movw ZL, r16 ; Load R16:R17 into Z.
|
||||
lds r18, NVM_CMD ; Store NVM command register (r18 is no longer needed)
|
||||
sts NVM_CMD, r20 ; Load prepared command into NVM Command register.
|
||||
ldi r19, CCP_SPM_gc ; Prepare Protect SPM signature (r19 is no longer needed)
|
||||
sts CCP, r19 ; Enable SPM operation (this disables interrupts for 4 cycles).
|
||||
spm ; Self-program.
|
||||
sts NVM_CMD, r18 ; Restore NVM command register
|
||||
out RAMPZ, r19 ; Restore RAMPZ register.
|
||||
#endif
|
||||
|
||||
ret
|
||||
|
||||
END_FUNC(nvm_common_spm)
|
||||
|
||||
// IAR forgets about include files after each module, so need to include again
|
||||
#if defined(__IAR_SYSTEMS_ASM__)
|
||||
# include <ioavr.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Load byte to page buffer
|
||||
*
|
||||
*/
|
||||
PUBLIC_FUNCTION_SEGMENT(nvm_flash_load_word_to_buffer, BOOT)
|
||||
|
||||
#if defined(__GNUC__)
|
||||
/**
|
||||
* For GCC:
|
||||
* \param word_addr uint32_t r22:r25
|
||||
* \param data uint16_t r20:r21
|
||||
*/
|
||||
wait_nvm:
|
||||
lds r18, NVM_STATUS
|
||||
sbrc r18, NVM_NVMBUSY_bp
|
||||
rjmp wait_nvm
|
||||
|
||||
in r25, RAMPZ ; Store RAMPZ. Highest address byte is ignored, so using that
|
||||
out RAMPZ, r24 ; Load R24 into RAMPZ
|
||||
movw ZL, r22 ; Load R22:R23 into Z.
|
||||
|
||||
lds r24, NVM_CMD ; Store NVM command register (r24 is no longer needed)
|
||||
ldi r18, NVM_CMD_LOAD_FLASH_BUFFER_gc
|
||||
sts NVM_CMD, r18 ; Load prepared command into NVM Command register.
|
||||
|
||||
movw r0, r20 ; Load R20:R21 into R0:R1
|
||||
spm ; Self-program.
|
||||
|
||||
clr r1 ; Clear R1 for GCC _zero_reg_ to function properly.
|
||||
sts NVM_CMD, r24 ; Restore NVM command register
|
||||
out RAMPZ, r25 ; Restore RAMPZ register.
|
||||
#elif defined(__IAR_SYSTEMS_ASM__)
|
||||
/**
|
||||
* For IAR:
|
||||
* \param word_addr uint32_t r16:r19
|
||||
* \param data uint16_t r20:r21
|
||||
*/
|
||||
wait_nvm:
|
||||
lds r19, NVM_STATUS
|
||||
sbrc r19, NVM_NVMBUSY_bp
|
||||
rjmp wait_nvm
|
||||
|
||||
in r19, RAMPZ ; Store RAMPZ. Highest byte is ignored, so using that
|
||||
out RAMPZ, r18 ; Load R18 into RAMPZ
|
||||
movw ZL, r16 ; Load R16:R17 into Z.
|
||||
|
||||
lds r18, NVM_CMD ; Store NVM command register (r18 is no longer needed)
|
||||
ldi r17, NVM_CMD_LOAD_FLASH_BUFFER_gc
|
||||
sts NVM_CMD, r17 ; Load prepared command into NVM Command register.
|
||||
|
||||
movw r0, r20 ; Load R20:R21 into R0:R1
|
||||
spm ; Self-program.
|
||||
|
||||
sts NVM_CMD, r18 ; Restore NVM command register
|
||||
out RAMPZ, r19 ; Restore RAMPZ register.
|
||||
#endif
|
||||
|
||||
ret
|
||||
|
||||
END_FUNC(nvm_flash_load_word_to_buffer)
|
||||
|
||||
END_FILE()
|
||||
#endif // __DOXYGEN__
|
||||
Reference in New Issue
Block a user