1094 lines
35 KiB
C
1094 lines
35 KiB
C
/**
|
|
* \file
|
|
*
|
|
* \brief Non Volatile Memory controller driver
|
|
*
|
|
* Copyright (c) 2010-2013 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
|
|
*
|
|
*/
|
|
#ifndef NVM_H
|
|
#define NVM_H
|
|
|
|
#include <compiler.h>
|
|
#include <ccp.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C"
|
|
{
|
|
#endif
|
|
|
|
/**
|
|
* \defgroup nvm_group NVM driver
|
|
*
|
|
* See \ref xmega_nvm_quickstart
|
|
*
|
|
* \brief Low-level driver implementation for the AVR XMEGA Non Volatile
|
|
* Memory Controller (NVM).
|
|
*
|
|
* The XMEGA NVM controller interfaces the internal non-volatile memories
|
|
* in the XMEGA devices. Program memory, EEPROM and signature row is can be
|
|
* interfaced by the module. See the documentation of each sub-module for
|
|
* more information.
|
|
*
|
|
* \note If using GCC and the flash sub-module, remember to configure
|
|
* the boot section in the make file. More information in the sub-module
|
|
* documentation.
|
|
*
|
|
* \section xmega_nvm_quickstart_section Quick Start Guide
|
|
* See \ref xmega_nvm_quickstart
|
|
*/
|
|
|
|
/**
|
|
* \defgroup nvm_generic_group NVM driver generic module handling
|
|
* \ingroup nvm_group
|
|
* \brief Support functions for the NVM driver.
|
|
*
|
|
* These functions are helper functions for the functions of the
|
|
* \ref nvm_group "NVM driver".
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* \brief Wait for any NVM access to finish.
|
|
*
|
|
* This function is blocking and waits for any NVM access to finish.
|
|
* Use this function before any NVM accesses, if you are not certain that
|
|
* any previous operations are finished yet.
|
|
*/
|
|
static inline void nvm_wait_until_ready (void)
|
|
{
|
|
do
|
|
{
|
|
// Block execution while waiting for the NVM to be ready
|
|
}
|
|
while ((NVM.STATUS & NVM_NVMBUSY_bm) == NVM_NVMBUSY_bm);
|
|
}
|
|
|
|
/**
|
|
* \brief Non-Volatile Memory Execute Command
|
|
*
|
|
* This function sets the CCP register before setting the CMDEX bit in the
|
|
* NVM.CTRLA register.
|
|
*
|
|
* \note The correct NVM command must be set in the NVM.CMD register before
|
|
* calling this function.
|
|
*/
|
|
static inline void nvm_exec (void)
|
|
{
|
|
ccp_write_io ((uint8_t *) & NVM.CTRLA, NVM_CMDEX_bm);
|
|
}
|
|
|
|
/**
|
|
* \brief Non-Volatile Memory Execute Specific Command
|
|
*
|
|
* This function sets a command in the NVM.CMD register, then performs an
|
|
* execute command by writing the CMDEX bit to the NVM.CTRLA register.
|
|
*
|
|
* \note 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 nvm_command NVM Command to execute.
|
|
*/
|
|
static inline void nvm_issue_command (NVM_CMD_t nvm_command)
|
|
{
|
|
uint8_t old_cmd;
|
|
|
|
old_cmd = NVM.CMD;
|
|
NVM.CMD = nvm_command;
|
|
ccp_write_io ((uint8_t *) & NVM.CTRLA, NVM_CMDEX_bm);
|
|
NVM.CMD = old_cmd;
|
|
}
|
|
|
|
/**
|
|
* \brief Read one byte using the LDI instruction
|
|
* \internal
|
|
*
|
|
* This function sets the specified NVM_CMD, reads one byte using at the
|
|
* specified byte address with the LPM instruction. NVM_CMD is restored after
|
|
* use.
|
|
*
|
|
* \note Interrupts should be disabled before running this function
|
|
* if program memory/NVM controller is accessed from ISRs.
|
|
*
|
|
* \param nvm_cmd NVM command to load before running LPM
|
|
* \param address Byte offset into the signature row
|
|
*/
|
|
uint8_t nvm_read_byte (uint8_t nvm_cmd, uint16_t address);
|
|
|
|
|
|
/**
|
|
* \brief Perform SPM write
|
|
* \internal
|
|
*
|
|
* This function sets the specified NVM_CMD, sets CCP and then runs the SPM
|
|
* instruction to write to flash.
|
|
*
|
|
* \note Interrupts should be disabled before running this function
|
|
* if program memory/NVM controller is accessed from ISRs.
|
|
*
|
|
* \param addr Address to perform the SPM on.
|
|
* \param nvm_cmd NVM command to use in the NVM_CMD register
|
|
*/
|
|
void nvm_common_spm (uint32_t addr, uint8_t nvm_cmd);
|
|
|
|
//! @}
|
|
|
|
/**
|
|
* \defgroup nvm_signature_group NVM driver signature handling
|
|
* \ingroup nvm_group
|
|
* \brief Handling of signature rows
|
|
*
|
|
* Functions for handling signature rows. The following is supported:
|
|
* - Reading values from production and user signature row
|
|
* - Reading device id
|
|
* - Reading device revision
|
|
* - Reading device serial
|
|
*
|
|
* \note Some of these functions are 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. See documentation for
|
|
* each individual function.
|
|
* \note Do not use the functions of this module in an interrupt service
|
|
* routine (ISR), since the functions can take several milliseconds to
|
|
* complete and hence block the interrupt for several milliseconds.
|
|
* In addition the functions of this module are modifying the page buffer
|
|
* which will corrupt any ongoing EEPROM handing used outside an ISR.
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* \brief Structure containing the device ID
|
|
*
|
|
* This structure can be used to store the device ID of a device.
|
|
*/
|
|
struct nvm_device_id
|
|
{
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
uint8_t devid0;
|
|
uint8_t devid1;
|
|
uint8_t devid2;
|
|
};
|
|
uint8_t byte[3];
|
|
};
|
|
};
|
|
|
|
/**
|
|
* \brief Structure containing the device serial
|
|
*
|
|
* This structure can be used to store the serial number of a device.
|
|
*/
|
|
struct nvm_device_serial
|
|
{
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
uint8_t lotnum0;
|
|
uint8_t lotnum1;
|
|
uint8_t lotnum2;
|
|
uint8_t lotnum3;
|
|
uint8_t lotnum4;
|
|
uint8_t lotnum5;
|
|
uint8_t wafnum;
|
|
uint8_t coordx0;
|
|
uint8_t coordx1;
|
|
uint8_t coordy0;
|
|
uint8_t coordy1;
|
|
};
|
|
uint8_t byte[11];
|
|
};
|
|
};
|
|
|
|
/**
|
|
* \brief Get offset of calibration bytes in the production signature row
|
|
*
|
|
* \param regname Name of register within the production signature row
|
|
* \retval Offset of register into the production signature row
|
|
*/
|
|
#if defined(__GNUC__)
|
|
# define nvm_get_production_signature_row_offset(regname) \
|
|
offsetof(NVM_PROD_SIGNATURES_t, regname)
|
|
#elif defined(__ICCAVR__)
|
|
# define nvm_get_production_signature_row_offset(regname) (regname##_offset)
|
|
#else
|
|
# error Unknown compiler
|
|
#endif
|
|
|
|
|
|
/**
|
|
* \brief Read one byte from the production signature row
|
|
*
|
|
* This function reads one byte from the production signature row of the device
|
|
* at the given address.
|
|
*
|
|
* \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.
|
|
*
|
|
* \param address Byte offset into the signature row
|
|
*/
|
|
static inline uint8_t nvm_read_production_signature_row (uint8_t address)
|
|
{
|
|
return nvm_read_byte (NVM_CMD_READ_CALIB_ROW_gc, address);
|
|
}
|
|
|
|
/**
|
|
* \brief Read one byte from the user signature row
|
|
*
|
|
* This function reads one byte from the user signature row of the device
|
|
* at the given address.
|
|
*
|
|
* \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.
|
|
*
|
|
* \param address Byte offset into the signature row
|
|
*/
|
|
static inline uint8_t nvm_read_user_signature_row (uint16_t address)
|
|
{
|
|
return nvm_read_byte (NVM_CMD_READ_USER_SIG_ROW_gc, address);
|
|
}
|
|
|
|
/**
|
|
* \brief Read the device id
|
|
*
|
|
* This function returns the device ID stored in the device.
|
|
*
|
|
* \retval storage Pointer to the structure where to store the device id
|
|
*/
|
|
static inline void nvm_read_device_id (struct nvm_device_id *storage)
|
|
{
|
|
storage->devid0 = MCU.DEVID0;
|
|
storage->devid1 = MCU.DEVID1;
|
|
storage->devid2 = MCU.DEVID2;
|
|
}
|
|
|
|
/**
|
|
* \brief Read the device revision
|
|
*
|
|
* This function returns the device revision stored in the device.
|
|
*
|
|
* \retval unsigned 8 bit value with the current device revision.
|
|
*/
|
|
static inline uint8_t nvm_read_device_rev (void)
|
|
{
|
|
return MCU.REVID;
|
|
}
|
|
|
|
void nvm_read_device_serial (struct nvm_device_serial *storage);
|
|
|
|
//! @}
|
|
|
|
|
|
/**
|
|
* \defgroup nvm_eeprom_group NVM driver EEPROM handling
|
|
* \ingroup nvm_group
|
|
* \brief Functions for handling internal EEPROM memory.
|
|
*
|
|
* The internal EEPROM can be used to store data that will persist after
|
|
* power is removed. This can typically be used to store calibration data,
|
|
* application state, encryption keys or other data that need to be preserved
|
|
* when power is removed.
|
|
*
|
|
* The functions in this module uses IO register access to manipulate the
|
|
* EEPROM.
|
|
*
|
|
* \note The functions in this module are 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.
|
|
* @{
|
|
*/
|
|
|
|
#ifndef EEPROM_PAGE_SIZE
|
|
# if XMEGA_A || XMEGA_AU || XMEGA_B || XMEGA_C || XMEGA_D || XMEGA_E
|
|
# define EEPROM_PAGE_SIZE 32
|
|
# else
|
|
# error Unknown EEPROM page size
|
|
# endif
|
|
#endif
|
|
|
|
#ifndef CONFIG_NVM_IGNORE_XMEGA_A3_D3_REVB_ERRATA
|
|
# if XMEGA_A3 || XMEGA_D3
|
|
# error This NVM driver does not support rev B of XMEGA A3/D3 devices. \
|
|
Set CONFIG_NVM_IGNORE_XMEGA_A3_D3_REVB_ERRATA to disable this message
|
|
# endif
|
|
#endif
|
|
|
|
/**
|
|
* Data type for holding eeprom memory addresses.
|
|
*/
|
|
typedef uint16_t eeprom_addr_t;
|
|
|
|
|
|
/*! \brief Enable EEPROM mapping into data space.
|
|
*
|
|
* This macro enables mapping of EEPROM into data space.
|
|
* EEPROM starts at EEPROM_START in data memory. Read access
|
|
* can be done similar to ordinary SRAM access.
|
|
*
|
|
* \note This disables IO-mapped access to EEPROM, although page erase and
|
|
* write operations still needs to be done through IO register.
|
|
*/
|
|
static inline void eeprom_enable_mapping (void)
|
|
{
|
|
#if !XMEGA_E
|
|
NVM_CTRLB = NVM_CTRLB | NVM_EEMAPEN_bm;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*! \brief Disable EEPROM mapping into data space.
|
|
*
|
|
* This macro disables mapping of EEPROM into data space.
|
|
* IO mapped access is now enabled.
|
|
*/
|
|
static inline void eeprom_disable_mapping (void)
|
|
{
|
|
#if !XMEGA_E
|
|
NVM_CTRLB = NVM_CTRLB & ~NVM_EEMAPEN_bm;
|
|
#endif
|
|
}
|
|
|
|
|
|
uint8_t nvm_eeprom_read_byte (eeprom_addr_t addr);
|
|
void nvm_eeprom_write_byte (eeprom_addr_t address, uint8_t value);
|
|
void nvm_eeprom_read_buffer (eeprom_addr_t address, void *buf,
|
|
uint16_t len);
|
|
void nvm_eeprom_erase_and_write_buffer (eeprom_addr_t address,
|
|
const void *buf, uint16_t len);
|
|
|
|
void nvm_eeprom_flush_buffer (void);
|
|
void nvm_eeprom_load_byte_to_buffer (uint8_t byte_addr, uint8_t value);
|
|
void nvm_eeprom_load_page_to_buffer (const uint8_t * values);
|
|
void nvm_eeprom_atomic_write_page (uint8_t page_addr);
|
|
void nvm_eeprom_split_write_page (uint8_t page_addr);
|
|
void nvm_eeprom_fill_buffer_with_value (uint8_t value);
|
|
void nvm_eeprom_erase_bytes_in_page (uint8_t page_addr);
|
|
void nvm_eeprom_erase_page (uint8_t page_addr);
|
|
void nvm_eeprom_erase_bytes_in_all_pages (void);
|
|
void nvm_eeprom_erase_all (void);
|
|
|
|
//! @}
|
|
|
|
/**
|
|
* \defgroup nvm_flash_group NVM driver flash handling
|
|
* \ingroup nvm_group
|
|
* \brief Functions for handling internal flash memory.
|
|
*
|
|
* The internal flash memory on the XMEGA devices consists of the application
|
|
* section, the application table section and the bootloader section.
|
|
* All these sections can store program code for the MCU, but if there is
|
|
* available space, they can be used for storing other persistent data.
|
|
*
|
|
* Writing the flash memory can only be done one page at a time. It consists
|
|
* of loading the data to the internal page buffer and then running one of
|
|
* the write commands. If the page has not been erased before writing, the
|
|
* data will not be written correctly.
|
|
*
|
|
* In order to be able to write to flash memory the programming commands need
|
|
* to be run from the boot section.
|
|
* - When using IAR this is handled automatically by the linker script.
|
|
* - When using GCC this needs to be specified manually in the make files. For
|
|
* example the ATxmega128A1 has the boot section at the word address 0x10000
|
|
* the corresponding byte address of 0x20000 needs to be added to the
|
|
* config.mk makefile:
|
|
* LDFLAGS += -Wl,--section-start=.BOOT=0x20000
|
|
* See the device datasheet for the correct address for other devices.
|
|
*
|
|
* \note If using GCC and the flash sub-module, remember to configure
|
|
* the boot section in the make file.
|
|
*
|
|
* \note The functions in this module are 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.
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* \brief Size of a flash page in bytes
|
|
*
|
|
* The page size in bytes taken from the toolchain header files.
|
|
*
|
|
* \note Page size is currently missing from the IAR header files, so it needs
|
|
* to be defined in the driver until it is fixed.
|
|
*/
|
|
#ifdef __DOXYGEN__
|
|
# define FLASH_SIZE
|
|
# define FLASH_PAGE_SIZE
|
|
#else
|
|
|
|
// 8K devices
|
|
# if AVR8_PART_IS_DEFINED(ATxmega8E5)
|
|
# define FLASH_SIZE (8*1024L)
|
|
# define FLASH_PAGE_SIZE (128)
|
|
|
|
// 16K devices
|
|
# elif AVR8_PART_IS_DEFINED(ATxmega16A4) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega16A4U) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega16D4) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega16C4)
|
|
# define FLASH_SIZE (16*1024L)
|
|
# define FLASH_PAGE_SIZE (256)
|
|
|
|
# elif AVR8_PART_IS_DEFINED(ATxmega16E5)
|
|
# define FLASH_SIZE (16*1024L)
|
|
# define FLASH_PAGE_SIZE (128)
|
|
|
|
// 32K devices
|
|
# elif AVR8_PART_IS_DEFINED(ATxmega32A4) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega32A4U) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega32D4) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega32C4)
|
|
# define FLASH_SIZE (32*1024L)
|
|
# define FLASH_PAGE_SIZE (256)
|
|
|
|
# elif AVR8_PART_IS_DEFINED(ATxmega32E5)
|
|
# define FLASH_SIZE (32*1024L)
|
|
# define FLASH_PAGE_SIZE (128)
|
|
|
|
// 64K devices
|
|
# elif AVR8_PART_IS_DEFINED(ATxmega64A1) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega64A1U) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega64A3) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega64A3U) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega64A4U) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega64B1) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega64B3) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega64C3) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega64D3) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega64D4)
|
|
# define FLASH_SIZE (64*1024L)
|
|
# define FLASH_PAGE_SIZE (256)
|
|
|
|
// 128K devices
|
|
# elif AVR8_PART_IS_DEFINED(ATxmega128A1) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega128A1U) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega128A3) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega128A3U) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega128C3) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega128D3) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega128D4)
|
|
# define FLASH_SIZE (128*1024L)
|
|
# define FLASH_PAGE_SIZE (512)
|
|
|
|
# elif AVR8_PART_IS_DEFINED(ATxmega128A4U) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega128B1) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega128B3)
|
|
# define FLASH_SIZE (128*1024L)
|
|
# define FLASH_PAGE_SIZE (256)
|
|
|
|
// 192K devices
|
|
# elif AVR8_PART_IS_DEFINED(ATxmega192A3U) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega192C3) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega192D3)
|
|
# define FLASH_SIZE (192*1024L)
|
|
# define FLASH_PAGE_SIZE (512)
|
|
|
|
// 256K devices
|
|
# elif AVR8_PART_IS_DEFINED(ATxmega256A3) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega256A3U) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega256A3B) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega256A3BU) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega256C3) | \
|
|
AVR8_PART_IS_DEFINED(ATxmega256D3)
|
|
# define FLASH_SIZE (256*1024L)
|
|
# define FLASH_PAGE_SIZE (512)
|
|
|
|
// 384K devices
|
|
# elif AVR8_PART_IS_DEFINED(ATxmega384C3)
|
|
# define FLASH_SIZE (384*1024L)
|
|
# define FLASH_PAGE_SIZE (512)
|
|
# elif AVR8_PART_IS_DEFINED(ATxmega384D3)
|
|
# define FLASH_SIZE (384*1024L)
|
|
# define FLASH_PAGE_SIZE (512)
|
|
# else
|
|
# error Flash page size needs to be defined.
|
|
# endif
|
|
#endif
|
|
|
|
/**
|
|
* Data type for holding flash memory addresses.
|
|
*
|
|
*/
|
|
#if (FLASH_SIZE >= 0x10000UL) // Note: Xmega with 64KB of flash have 4KB boot flash
|
|
typedef uint32_t flash_addr_t;
|
|
#else
|
|
typedef uint16_t flash_addr_t;
|
|
#endif
|
|
|
|
/**
|
|
* Flash pointer type to use for accessing flash memory with IAR
|
|
*/
|
|
#if (FLASH_SIZE >= 0x10000UL) // Note: Xmega with 64KB of flash have 4KB boot flash
|
|
# define IAR_FLASH_PTR __farflash
|
|
#else
|
|
# define IAR_FLASH_PTR __flash
|
|
#endif
|
|
|
|
/**
|
|
* \brief Load byte from flash memory
|
|
*
|
|
* Load one word of flash using byte addressing. IAR has __flash pointers
|
|
* and GCC have pgm_read_byte_xx functions which load data from flash memory.
|
|
* This function used for compatibility between the compilers.
|
|
*
|
|
* \param addr Byte address to load
|
|
* \return Byte from program memory
|
|
*/
|
|
static inline uint8_t nvm_flash_read_byte (flash_addr_t addr)
|
|
{
|
|
#if defined(__GNUC__)
|
|
return pgm_read_byte_far (addr);
|
|
#elif defined(__ICCAVR__)
|
|
uint8_t IAR_FLASH_PTR *flashptr = (uint8_t IAR_FLASH_PTR *) addr;
|
|
return *flashptr;
|
|
#else
|
|
# error Unknown compiler
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* \brief Load word from flash memory
|
|
*
|
|
* Load one word of flash using byte addressing. IAR has __flash pointers
|
|
* and GCC have pgm_read_byte_xx functions which load data from flash memory.
|
|
* This function used for compatibility between the compilers.
|
|
*
|
|
* \param addr Byte address to load (last bit is ignored)
|
|
* \return Word from program memory
|
|
*/
|
|
static inline uint16_t nvm_flash_read_word (flash_addr_t addr)
|
|
{
|
|
#if defined(__GNUC__)
|
|
return pgm_read_word_far (addr);
|
|
#elif defined(__ICCAVR__)
|
|
uint16_t IAR_FLASH_PTR *flashptr = (uint16_t IAR_FLASH_PTR *) addr;
|
|
return *flashptr;
|
|
#endif
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief Flush flash page buffer
|
|
*
|
|
* Clear the NVM controller page buffer for flash. This needs to be called
|
|
* before using \ref nvm_flash_load_word_to_buffer if it has not already been
|
|
* cleared.
|
|
*
|
|
*/
|
|
static inline void nvm_flash_flush_buffer (void)
|
|
{
|
|
nvm_wait_until_ready ();
|
|
nvm_common_spm (0, NVM_CMD_ERASE_FLASH_BUFFER_gc);
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief Load word into flash page buffer
|
|
*
|
|
* Clear the NVM controller page buffer for flash. This needs to be called
|
|
* before using \ref nvm_flash_load_word_to_buffer if it has not already been
|
|
* cleared.
|
|
*
|
|
* \param word_addr Address to store data. The upper bits beyond the page size
|
|
* is ignored. \ref FLASH_PAGE_SIZE
|
|
* \param data Data word to load into the page buffer
|
|
*/
|
|
void nvm_flash_load_word_to_buffer (uint32_t word_addr, uint16_t data);
|
|
|
|
|
|
/**
|
|
* \brief Erase entire application section
|
|
*
|
|
* Erase all of the application section.
|
|
*/
|
|
static inline void nvm_flash_erase_app (void)
|
|
{
|
|
nvm_wait_until_ready ();
|
|
nvm_common_spm (0, NVM_CMD_ERASE_APP_gc);
|
|
}
|
|
|
|
/**
|
|
* \brief Erase a page within the application section
|
|
*
|
|
* Erase one page within the application section
|
|
*
|
|
* \param page_addr Byte address to the page to delete
|
|
*/
|
|
static inline void nvm_flash_erase_app_page (flash_addr_t page_addr)
|
|
{
|
|
nvm_wait_until_ready ();
|
|
nvm_common_spm (page_addr, NVM_CMD_ERASE_APP_PAGE_gc);
|
|
}
|
|
|
|
/**
|
|
* \brief Write a page within the application section
|
|
*
|
|
* Write a page within the application section with the data stored in the
|
|
* page buffer. The page needs to be erased before the write to avoid
|
|
* corruption of the data written.
|
|
*
|
|
* \param page_addr Byte address to the page to delete
|
|
*/
|
|
static inline void nvm_flash_split_write_app_page (flash_addr_t page_addr)
|
|
{
|
|
nvm_wait_until_ready ();
|
|
nvm_common_spm (page_addr, NVM_CMD_WRITE_APP_PAGE_gc);
|
|
}
|
|
|
|
/**
|
|
* \brief Erase and write a page within the application section
|
|
*
|
|
* Erase and the write a page within the application section with the data
|
|
* stored in the page buffer. Erase and write is done in an atomic operation.
|
|
*
|
|
* \param page_addr Byte address to the page to delete
|
|
*/
|
|
static inline void nvm_flash_atomic_write_app_page (flash_addr_t page_addr)
|
|
{
|
|
nvm_wait_until_ready ();
|
|
nvm_common_spm (page_addr, NVM_CMD_ERASE_WRITE_APP_PAGE_gc);
|
|
}
|
|
|
|
void nvm_issue_flash_range_crc (flash_addr_t start_addr,
|
|
flash_addr_t end_addr);
|
|
|
|
void nvm_flash_read_buffer (flash_addr_t address, void *buf, uint16_t len);
|
|
|
|
void nvm_flash_erase_and_write_buffer (flash_addr_t address,
|
|
const void *buf, uint16_t len,
|
|
bool b_blank_check);
|
|
|
|
/**
|
|
* \brief Erase a page within the boot section
|
|
*
|
|
* Erase one page within the boot section
|
|
*
|
|
* \param page_addr Byte address to the page to delete
|
|
*/
|
|
static inline void nvm_flash_erase_boot_page (flash_addr_t page_addr)
|
|
{
|
|
nvm_wait_until_ready ();
|
|
nvm_common_spm (page_addr, NVM_CMD_ERASE_BOOT_PAGE_gc);
|
|
}
|
|
|
|
/**
|
|
* \brief Write a page within the boot section
|
|
*
|
|
* Write a page within the boot section with the data stored in the
|
|
* page buffer. The page needs to be erased before the write to avoid
|
|
* corruption of the data written.
|
|
*
|
|
* \param page_addr Byte address to the page to delete
|
|
*/
|
|
static inline void nvm_flash_split_write_boot_page (flash_addr_t page_addr)
|
|
{
|
|
nvm_wait_until_ready ();
|
|
nvm_common_spm (page_addr, NVM_CMD_WRITE_BOOT_PAGE_gc);
|
|
}
|
|
|
|
/**
|
|
* \brief Erase and write a page within the boot section
|
|
*
|
|
* Erase and the write a page within the boot section with the data
|
|
* stored in the page buffer. Erase and write is done in an atomic operation.
|
|
*
|
|
* \param page_addr Byte address to the page to delete
|
|
*/
|
|
static inline void nvm_flash_atomic_write_boot_page (flash_addr_t page_addr)
|
|
{
|
|
nvm_wait_until_ready ();
|
|
nvm_common_spm (page_addr, NVM_CMD_ERASE_WRITE_BOOT_PAGE_gc);
|
|
}
|
|
|
|
void nvm_user_sig_read_buffer (flash_addr_t address, void *buf,
|
|
uint16_t len);
|
|
void nvm_user_sig_write_buffer (flash_addr_t address, const void *buf,
|
|
uint16_t len, bool b_blank_check);
|
|
|
|
/**
|
|
* \brief Erase the user calibration section page
|
|
*
|
|
* Erase the user calibration section page. There is only one page, so no
|
|
* parameters are needed.
|
|
*/
|
|
static inline void nvm_flash_erase_user_section (void)
|
|
{
|
|
nvm_wait_until_ready ();
|
|
nvm_common_spm (0, NVM_CMD_ERASE_USER_SIG_ROW_gc);
|
|
}
|
|
|
|
/**
|
|
* \brief Write the user calibration section page
|
|
*
|
|
* Write a the user calibration section page with the data stored in the
|
|
* page buffer. The page needs to be erased before the write to avoid
|
|
* corruption of the data written. There is only one page, so no
|
|
* parameters are needed.
|
|
*/
|
|
static inline void nvm_flash_write_user_page (void)
|
|
{
|
|
nvm_wait_until_ready ();
|
|
nvm_common_spm (0, NVM_CMD_WRITE_USER_SIG_ROW_gc);
|
|
}
|
|
|
|
//! @}
|
|
|
|
/**
|
|
* \defgroup nvm_fuse_lock_group NVM driver fuse and lock bits handling
|
|
* \ingroup nvm_group
|
|
* \brief Functions for reading fuses and writing lock bits.
|
|
*
|
|
* The Fuses are used to set important system functions and can only be written
|
|
* from an external programming interface. The application software can read
|
|
* the fuses. The fuses are used to configure reset sources such as Brown-out
|
|
* Detector and Watchdog, Start-up configuration, JTAG enable and JTAG user ID.
|
|
*
|
|
* The Lock bits are used to set protection level on the different flash
|
|
* sections. They are used to block read and/or write on the different flash
|
|
* sections. Lock bits can be written from en external programmer and from the
|
|
* application software to set a more strict protection level, but not to set a
|
|
* less strict protection level. Chip erase is the only way to erase the lock
|
|
* bits. The lock bits are erased after the rest of the flash memory is erased.
|
|
* An unprogrammed fuse or lock bit will have the value one, while a programmed
|
|
* fuse or lock bit will have the value zero.
|
|
* Both fuses and lock bits are reprogrammable like the Flash Program memory.
|
|
*
|
|
* \note The functions in this module are 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.
|
|
* @{
|
|
*/
|
|
|
|
// The different fuse bytes
|
|
enum fuse_byte_t
|
|
{
|
|
FUSEBYTE0 = 0,
|
|
FUSEBYTE1 = 1,
|
|
FUSEBYTE2 = 2,
|
|
FUSEBYTE3 = 3, // not used on current devices
|
|
FUSEBYTE4 = 4,
|
|
FUSEBYTE5 = 5,
|
|
};
|
|
|
|
uint8_t nvm_fuses_read (enum fuse_byte_t fuse);
|
|
|
|
/**
|
|
* \brief Program the lock bits.
|
|
*
|
|
* Program the lock bits to the given values. Lock bits can only be programmed
|
|
* to a more secure setting than previously programmed. To clear lock bits, a
|
|
* flash erase has to be issued.
|
|
*
|
|
* \param blbb_lock Boot loader section lock bits to program
|
|
* \param blba_lock Application section lock bits to program
|
|
* \param blbat_lock Application table section lock bits to program
|
|
* \param lb_lock Flash/eeprom lock bits to program
|
|
*/
|
|
static inline void nvm_lock_bits_write (enum NVM_BLBB_enum blbb_lock,
|
|
enum NVM_BLBA_enum blba_lock,
|
|
enum NVM_BLBAT_enum blbat_lock,
|
|
enum NVM_LB_enum lb_lock)
|
|
{
|
|
nvm_wait_until_ready ();
|
|
NVM.DATA0 =
|
|
(uint8_t) blbb_lock | (uint8_t) blba_lock | (uint8_t) blbat_lock |
|
|
(uint8_t) lb_lock;
|
|
nvm_issue_command (NVM_CMD_WRITE_LOCK_BITS_gc);
|
|
}
|
|
|
|
/**
|
|
* \brief Program the BLBB lock bits.
|
|
*
|
|
* Program the lock bits for the boot loader section (BLBB). Other lock bits
|
|
* (BLBA, BLBAT and LB) are not altered (ie. programmed to NOLOCK).
|
|
*
|
|
* \param blbb_lock Boot loader section lock bits to program
|
|
*/
|
|
static inline void nvm_blbb_lock_bits_write (enum NVM_BLBB_enum blbb_lock)
|
|
{
|
|
nvm_lock_bits_write (blbb_lock, NVM_BLBA_NOLOCK_gc, NVM_BLBAT_NOLOCK_gc,
|
|
NVM_LB_NOLOCK_gc);
|
|
}
|
|
|
|
/**
|
|
* \brief Program the BLBA lock bits.
|
|
*
|
|
* Program the lock bits for the application section (BLBA). Other lock bits
|
|
* (BLBB, BLBAT and LB) are not altered (ie. programmed to NOLOCK).
|
|
*
|
|
* \param blba_lock Application section lock bits to program
|
|
*/
|
|
static inline void nvm_blba_lock_bits_write (enum NVM_BLBA_enum blba_lock)
|
|
{
|
|
nvm_lock_bits_write (NVM_BLBB_NOLOCK_gc, blba_lock, NVM_BLBAT_NOLOCK_gc,
|
|
NVM_LB_NOLOCK_gc);
|
|
}
|
|
|
|
/**
|
|
* \brief Program the BLBAT lock bits.
|
|
*
|
|
* Program the lock bits for the application table section (BLBAT). Other lock
|
|
* bits (BLBB, BLBA and LB) are not altered (ie. programmed to NOLOCK).
|
|
*
|
|
* \param blbat_lock Application table section lock bits to program
|
|
*/
|
|
static inline void nvm_blbat_lock_bits_write (enum NVM_BLBAT_enum
|
|
blbat_lock)
|
|
{
|
|
nvm_lock_bits_write (NVM_BLBB_NOLOCK_gc, NVM_BLBA_NOLOCK_gc, blbat_lock,
|
|
NVM_LB_NOLOCK_gc);
|
|
}
|
|
|
|
/**
|
|
* \brief Program the LB lock bits.
|
|
*
|
|
* Program the lock bits for the flash and eeprom (LB). Other lock bits
|
|
* (BLBB, BLBA and BLBAT) are not altered (ie. programmed to NOLOCK).
|
|
*
|
|
* \param lb_lock Flash/eeprom lock bits to program
|
|
*/
|
|
static inline void nvm_lb_lock_bits_write (enum NVM_LB_enum lb_lock)
|
|
{
|
|
nvm_lock_bits_write (NVM_BLBB_NOLOCK_gc, NVM_BLBA_NOLOCK_gc,
|
|
NVM_BLBAT_NOLOCK_gc, lb_lock);
|
|
}
|
|
|
|
//! @}
|
|
|
|
/**
|
|
* \page xmega_nvm_quickstart Quick Start Guide for the XMEGA NVM Driver
|
|
*
|
|
* This is the quick start guide for the \ref nvm_group "NVM Driver", with
|
|
* step-by-step instructions on how to configure and use the driver for
|
|
* specific use cases.
|
|
*
|
|
* The section described below can be compiled into e.g. the main application
|
|
* loop or any other function that will need to interface non-volatile memory.
|
|
*
|
|
* \section xmega_nvm_quickstart_basic Basic usage of the NVM driver
|
|
* This section will present three use cases of the NVM driver. The first will
|
|
* write a page to EEPROM and verify that it has been written, the second will
|
|
* access the BOD-level fuse to verify that the level is correctly set, and the
|
|
* third will read a chunk from the user signature row.
|
|
*
|
|
* \section xmega_nvm_quickstart_eeprom_case Use case 1: EEPROM
|
|
*
|
|
* The NVM driver has functions for interfacing many types of non-volatile
|
|
* memory, including flash, EEPROM, fuses and lock bits. The example code
|
|
* below will write a page to the internal EEPROM, and read it back to verify,
|
|
* using memory mapped I/O.
|
|
*
|
|
* \section xmega_nvm_quickstart_eeprom_case_setup_steps Setup steps
|
|
* There are no setup steps required for this use case.
|
|
*
|
|
* \subsection nvm_quickstart_eeprom_case_example_code Example code
|
|
*
|
|
* \code
|
|
* #define EXAMPLE_PAGE 2
|
|
* #define EXAMPLE_ADDR EXAMPLE_PAGE * EEPROM_PAGE_SIZE
|
|
*
|
|
* uint8_t write_page[EEPROM_PAGE_SIZE];
|
|
* uint8_t read_page[EEPROM_PAGE_SIZE];
|
|
*
|
|
* fill_page_with_known_data(write_page);
|
|
* fill_page_with_zeroes(read_page);
|
|
*
|
|
* nvm_eeprom_load_page_to_buffer(write_page);
|
|
* nvm_eeprom_atomic_write_page(EXAMPLE_PAGE);
|
|
*
|
|
* nvm_eeprom_read_buffer(EXAMPLE_ADDR,
|
|
* read_page, EEPROM_PAGE_SIZE);
|
|
*
|
|
* check_if_pages_are_equal(write_page, read_page);
|
|
* \endcode
|
|
*
|
|
* \subsection nvm_quickstart_eeprom_case_workflow Workflow
|
|
*
|
|
* -# We define where we would like to store our data, and we arbitrarily
|
|
* choose page 2 of EEPROM:
|
|
* - \code
|
|
* #define EXAMPLE_PAGE 2
|
|
* #define EXAMPLE_ADDR EXAMPLE_PAGE * EEPROM_PAGE_SIZE
|
|
* \endcode
|
|
* -# Define two tables, one which contains the data which we will write,
|
|
* and one which we will read the data into:
|
|
* - \code
|
|
* uint8_t write_page[EEPROM_PAGE_SIZE];
|
|
* uint8_t read_page[EEPROM_PAGE_SIZE];
|
|
* \endcode
|
|
* -# Fill the tables with our data, and zero out the read table:
|
|
* - \code
|
|
* fill_page_with_known_data(write_page);
|
|
* fill_page_with_zeroes(read_page);
|
|
* \endcode
|
|
* - \note These functions are undeclared, you should replace them with
|
|
* your own appropriate functions.
|
|
* -# We load our page into a temporary EEPROM page buffer:
|
|
* - \code
|
|
* nvm_eeprom_load_page_to_buffer(write_page);
|
|
* \endcode
|
|
* - \attention The function used above will not work if memory mapping
|
|
* is enabled.
|
|
* -# Do an atomic write of the page from buffer into the specified page:
|
|
* - \code
|
|
* nvm_eeprom_atomic_write_page(EXAMPLE_PAGE);
|
|
* \endcode
|
|
* - \note The function \ref nvm_eeprom_atomic_write_page() erases the
|
|
* page before writing the new one. For non-atomic (split)
|
|
* writing without deleting, see \ref nvm_eeprom_split_write_page()
|
|
* -# Read the page back into our read_page[] table:
|
|
* - \code
|
|
* nvm_eeprom_read_buffer(EXAMPLE_ADDR,
|
|
* read_page, EEPROM_PAGE_SIZE);
|
|
* \endcode
|
|
* -# Verify that the page is equal to the one that was written earlier:
|
|
* - \code
|
|
* check_if_pages_are_equal(write_page, read_page);
|
|
* \endcode
|
|
* - \note This function is not declared, you should replace it with your
|
|
* own appropriate function.
|
|
*
|
|
* \section xmega_nvm_quickstart_fuse_case Use case 2: Fuses
|
|
*
|
|
* The NVM driver has functions for reading fuses.
|
|
* See \ref nvm_fuse_lock_group.
|
|
*
|
|
* We would like to check whether the Brown-out Detection level is set to
|
|
* 2.1V. This is set by programming the fuses when the chip is connected
|
|
* to a suitable programmer. The fuse is a part of FUSEBYTE5. If the BODLVL
|
|
* is correct, we turn on LED0.
|
|
*
|
|
* \section xmega_nvm_quickstart_fuse_case_setup_steps Setup steps
|
|
* There are no setup steps required for this use case.
|
|
*
|
|
* \subsection nvm_quickstart_fuse_case_example_code Example code
|
|
* \code
|
|
* uint8_t fuse_value;
|
|
* fuse_value = nvm_fuses_read(FUSEBYTE5);
|
|
*
|
|
* if ((fuse_value & NVM_FUSES_BODLVL_gm) == BODLVL_2V1_gc) {
|
|
* gpio_set_pin_low(LED0_GPIO);
|
|
* }
|
|
* \endcode
|
|
*
|
|
* \subsection nvm_quickstart_fuse_case_workflow Workflow
|
|
*
|
|
* -# Create a variable to store the fuse contents:
|
|
* - \code
|
|
* uint8_t fuse_value;
|
|
* \endcode
|
|
* -# The fuse value we are interested in, BODLVL, is stored in FUSEBYTE5.
|
|
* We call the function \ref nvm_fuses_read() to read the fuse into our
|
|
* variable:
|
|
* - \code
|
|
* fuse_value = nvm_fuses_read(FUSEBYTE5);
|
|
* \endcode
|
|
* -# This ends the reading portion, but we would like to see whether the
|
|
* BOD-level is correct, and if it is, light up an LED:
|
|
* - \code
|
|
* if ((fuse_value & NVM_FUSES_BODLVL_gm) == BODLVL_2V1_gc) {
|
|
* gpio_set_pin_low(LED0_GPIO);
|
|
* }
|
|
* \endcode
|
|
*
|
|
* \section xmega_nvm_quickstart_signature_case Use case 3: Signature row
|
|
*
|
|
* The NVM driver has functions for reading the signature row of the device.
|
|
* Here we will simply read 16 bytes from the user signature row, assuming
|
|
* we need what is stored there.
|
|
*
|
|
* \section xmega_nvm_quickstart_signature_row_setup_steps Setup steps
|
|
* There are no setup steps required for this use case.
|
|
*
|
|
* \subsection xmega_nvm_quickstart_signature_row_example_code Example code
|
|
*
|
|
* \code
|
|
* #define START_ADDR 0x10
|
|
* #define DATA_LENGTH 16
|
|
*
|
|
* uint8_t values[LENGTH];
|
|
* uint8_t i;
|
|
*
|
|
* for (i = 0; i < DATA_LENGTH; i++) {
|
|
* values[i] = nvm_read_user_signature_row(START_ADDR + i);
|
|
* }
|
|
* \endcode
|
|
*
|
|
* \subsection nvm_quickstart_signature_case_workflow Workflow
|
|
*
|
|
* -# Define starting address and length of data segment, and create
|
|
* variables needed to store and process the data:
|
|
* - \code
|
|
* #define START_ADDR 0x10
|
|
* #define DATA_LENGTH 16
|
|
*
|
|
* uint8_t values[LENGTH];
|
|
* uint8_t i;
|
|
* \endcode
|
|
* -# Iterate through the user signature row, and store our desired data:
|
|
* - \code
|
|
* for (i = 0; i < DATA_LENGTH; i++) {
|
|
* values[i] = nvm_read_user_signature_row(START_ADDR + i);
|
|
* }
|
|
* \endcode
|
|
*
|
|
*/
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* NVM_H */
|