aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--atmel-samd/Makefile11
-rw-r--r--atmel-samd/asf/sam0/drivers/nvm/nvm.c1146
-rw-r--r--atmel-samd/asf/sam0/drivers/nvm/nvm.h944
-rw-r--r--atmel-samd/asf/version.h3
-rw-r--r--atmel-samd/boards/samd21x18-bootloader.ld2
-rw-r--r--atmel-samd/boards/samd21x18.ld2
-rw-r--r--atmel-samd/builtin_open.c30
-rw-r--r--atmel-samd/fatfs_port.c46
-rw-r--r--atmel-samd/main.c123
-rw-r--r--atmel-samd/modmachine.c2
-rw-r--r--atmel-samd/moduos.c385
-rw-r--r--atmel-samd/mpconfigport.h27
-rw-r--r--atmel-samd/storage.c290
-rw-r--r--atmel-samd/storage.h47
14 files changed, 3046 insertions, 12 deletions
diff --git a/atmel-samd/Makefile b/atmel-samd/Makefile
index f57f837cf..a4b08a59d 100644
--- a/atmel-samd/Makefile
+++ b/atmel-samd/Makefile
@@ -35,6 +35,7 @@ HAL_DIR=hal/$(MCU_SERIES)
INC += -I.
INC += -I..
INC += -I../lib/mp-readline
+INC += -I../lib/timeutils
INC += -Iasf/common/boards/
INC += -Iasf/common/services/sleepmgr/
INC += -Iasf/common/services/usb/
@@ -99,7 +100,7 @@ endif
#Debugging/Optimization
ifeq ($(DEBUG), 1)
-CFLAGS += -O0 -ggdb
+CFLAGS += -Os -ggdb
else
CFLAGS += -Os -DNDEBUG
endif
@@ -119,6 +120,7 @@ endif
SRC_ASF = $(addprefix asf/sam0/,\
drivers/adc/adc_sam_d_r/adc.c \
drivers/dac/dac_sam_d_c/dac.c \
+ drivers/nvm/nvm.c \
drivers/port/port.c \
drivers/sercom/sercom.c \
drivers/sercom/sercom_interrupt.c \
@@ -137,15 +139,19 @@ SRC_ASF = $(addprefix asf/sam0/,\
)
SRC_C = \
+ builtin_open.c \
+ fatfs_port.c \
main.c \
modmachine.c \
modmachine_adc.c \
modmachine_dac.c \
modmachine_pin.c \
modmachine_pwm.c \
+ moduos.c \
modutime.c \
mphalport.c \
pin_named_pins.c \
+ storage.c \
uart.c \
asf/common/services/sleepmgr/samd/sleepmgr.c \
asf/common/services/usb/class/cdc/device/udi_cdc.c \
@@ -157,6 +163,9 @@ SRC_C = \
asf/sam0/utils/cmsis/samd21/source/system_samd21.c \
asf/sam0/utils/syscalls/gcc/syscalls.c \
boards/$(BOARD)/pins.c \
+ lib/fatfs/ff.c \
+ lib/fatfs/option/ccsbcs.c \
+ lib/timeutils/timeutils.c \
lib/utils/stdout_helpers.c \
lib/utils/printf.c \
lib/utils/pyexec.c \
diff --git a/atmel-samd/asf/sam0/drivers/nvm/nvm.c b/atmel-samd/asf/sam0/drivers/nvm/nvm.c
new file mode 100644
index 000000000..53a0d7beb
--- /dev/null
+++ b/atmel-samd/asf/sam0/drivers/nvm/nvm.c
@@ -0,0 +1,1146 @@
+/**
+ * \file
+ *
+ * \brief SAM Non Volatile Memory driver
+ *
+ * Copyright (C) 2012-2016 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
+ *
+ */
+/*
+ * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+#include "nvm.h"
+#include <system.h>
+#include <system_interrupt.h>
+#include <string.h>
+
+/**
+ * \internal Internal device instance struct
+ *
+ * This struct contains information about the NVM module which is
+ * often used by the different functions. The information is loaded
+ * into the struct in the nvm_init() function.
+ */
+struct _nvm_module {
+ /** Number of bytes contained per page. */
+ uint16_t page_size;
+ /** Total number of pages in the NVM memory. */
+ uint16_t number_of_pages;
+ /** If \c false, a page write command will be issued automatically when the
+ * page buffer is full. */
+ bool manual_page_write;
+};
+
+/**
+ * \internal Instance of the internal device struct
+ */
+static struct _nvm_module _nvm_dev;
+
+/**
+ * \internal Pointer to the NVM MEMORY region start address
+ */
+#define NVM_MEMORY ((volatile uint16_t *)FLASH_ADDR)
+
+/**
+ * \internal Pointer to the NVM USER MEMORY region start address
+ */
+#define NVM_USER_MEMORY ((volatile uint16_t *)NVMCTRL_USER)
+
+
+/**
+ * \brief Sets the up the NVM hardware module based on the configuration.
+ *
+ * Writes a given configuration of an NVM controller configuration to the
+ * hardware module, and initializes the internal device struct.
+ *
+ * \param[in] config Configuration settings for the NVM controller
+ *
+ * \note The security bit must be cleared in order successfully use this
+ * function. This can only be done by a chip erase.
+ *
+ * \return Status of the configuration procedure.
+ *
+ * \retval STATUS_OK If the initialization was a success
+ * \retval STATUS_BUSY If the module was busy when the operation was attempted
+ * \retval STATUS_ERR_IO If the security bit has been set, preventing the
+ * EEPROM and/or auxiliary space configuration from being
+ * altered
+ */
+enum status_code nvm_set_config(
+ const struct nvm_config *const config)
+{
+ /* Sanity check argument */
+ Assert(config);
+
+ /* Get a pointer to the module hardware instance */
+ Nvmctrl *const nvm_module = NVMCTRL;
+
+#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30)
+ /* Turn on the digital interface clock */
+ system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBB, MCLK_APBBMASK_NVMCTRL);
+#else
+ /* Turn on the digital interface clock */
+ system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBB, PM_APBBMASK_NVMCTRL);
+#endif
+
+ /* Clear error flags */
+ nvm_module->STATUS.reg = NVMCTRL_STATUS_MASK;
+
+ /* Check if the module is busy */
+ if (!nvm_is_ready()) {
+ return STATUS_BUSY;
+ }
+
+#if (!SAMC20) && (!SAMC21)
+ /* Writing configuration to the CTRLB register */
+ nvm_module->CTRLB.reg =
+ NVMCTRL_CTRLB_SLEEPPRM(config->sleep_power_mode) |
+ ((config->manual_page_write & 0x01) << NVMCTRL_CTRLB_MANW_Pos) |
+ NVMCTRL_CTRLB_RWS(config->wait_states) |
+ ((config->disable_cache & 0x01) << NVMCTRL_CTRLB_CACHEDIS_Pos) |
+ NVMCTRL_CTRLB_READMODE(config->cache_readmode);
+#else
+ uint8_t cache_disable_value = 0;
+ if (config->disable_rww_cache == false) {
+ cache_disable_value = 0x02;
+ } else {
+ cache_disable_value = (config->disable_cache & 0x01);
+ }
+ /* Writing configuration to the CTRLB register */
+ nvm_module->CTRLB.reg =
+ NVMCTRL_CTRLB_SLEEPPRM(config->sleep_power_mode) |
+ ((config->manual_page_write & 0x01) << NVMCTRL_CTRLB_MANW_Pos) |
+ NVMCTRL_CTRLB_RWS(config->wait_states) |
+ (cache_disable_value << NVMCTRL_CTRLB_CACHEDIS_Pos) |
+ NVMCTRL_CTRLB_READMODE(config->cache_readmode);
+#endif
+
+ /* Initialize the internal device struct */
+ _nvm_dev.page_size = (8 << nvm_module->PARAM.bit.PSZ);
+ _nvm_dev.number_of_pages = nvm_module->PARAM.bit.NVMP;
+ _nvm_dev.manual_page_write = config->manual_page_write;
+
+ /* If the security bit is set, the auxiliary space cannot be written */
+ if (nvm_module->STATUS.reg & NVMCTRL_STATUS_SB) {
+ return STATUS_ERR_IO;
+ }
+
+ return STATUS_OK;
+}
+
+/**
+ * \brief Executes a command on the NVM controller.
+ *
+ * Executes an asynchronous command on the NVM controller, to perform a requested
+ * action such as an NVM page read or write operation.
+ *
+ * \note The function will return before the execution of the given command is
+ * completed.
+ *
+ * \param[in] command Command to issue to the NVM controller
+ * \param[in] address Address to pass to the NVM controller in NVM memory
+ * space
+ * \param[in] parameter Parameter to pass to the NVM controller, not used
+ * for this driver
+ *
+ * \return Status of the attempt to execute a command.
+ *
+ * \retval STATUS_OK If the command was accepted and execution
+ * is now in progress
+ * \retval STATUS_BUSY If the NVM controller was already busy
+ * executing a command when the new command
+ * was issued
+ * \retval STATUS_ERR_IO If the command was invalid due to memory or
+ * security locking
+ * \retval STATUS_ERR_INVALID_ARG If the given command was invalid or
+ * unsupported
+ * \retval STATUS_ERR_BAD_ADDRESS If the given address was invalid
+ */
+enum status_code nvm_execute_command(
+ const enum nvm_command command,
+ const uint32_t address,
+ const uint32_t parameter)
+{
+ uint32_t ctrlb_bak;
+
+ /* Check that the address given is valid */
+ if (address > ((uint32_t)_nvm_dev.page_size * _nvm_dev.number_of_pages)
+ && !(address >= NVMCTRL_AUX0_ADDRESS && address <= NVMCTRL_AUX1_ADDRESS )){
+#ifdef FEATURE_NVM_RWWEE
+ if (address >= ((uint32_t)NVMCTRL_RWW_EEPROM_SIZE + NVMCTRL_RWW_EEPROM_ADDR)
+ || address < NVMCTRL_RWW_EEPROM_ADDR){
+ return STATUS_ERR_BAD_ADDRESS;
+ }
+#else
+ return STATUS_ERR_BAD_ADDRESS;
+#endif
+ }
+
+ /* Get a pointer to the module hardware instance */
+ Nvmctrl *const nvm_module = NVMCTRL;
+
+ /* Turn off cache before issuing flash commands */
+ ctrlb_bak = nvm_module->CTRLB.reg;
+#if (SAMC20) || (SAMC21)
+ nvm_module->CTRLB.reg = ((ctrlb_bak &(~(NVMCTRL_CTRLB_CACHEDIS(0x2))))
+ | NVMCTRL_CTRLB_CACHEDIS(0x1));
+#else
+ nvm_module->CTRLB.reg = ctrlb_bak | NVMCTRL_CTRLB_CACHEDIS;
+#endif
+
+ /* Clear error flags */
+ nvm_module->STATUS.reg = NVMCTRL_STATUS_MASK;
+
+ /* Check if the module is busy */
+ if (!nvm_is_ready()) {
+ /* Restore the setting */
+ nvm_module->CTRLB.reg = ctrlb_bak;
+ return STATUS_BUSY;
+ }
+
+ switch (command) {
+
+ /* Commands requiring address (protected) */
+ case NVM_COMMAND_ERASE_AUX_ROW:
+ case NVM_COMMAND_WRITE_AUX_ROW:
+
+ /* Auxiliary space cannot be accessed if the security bit is set */
+ if (nvm_module->STATUS.reg & NVMCTRL_STATUS_SB) {
+ /* Restore the setting */
+ nvm_module->CTRLB.reg = ctrlb_bak;
+ return STATUS_ERR_IO;
+ }
+
+ /* Set address, command will be issued elsewhere */
+ nvm_module->ADDR.reg = (uintptr_t)&NVM_MEMORY[address / 4];
+ break;
+
+ /* Commands requiring address (unprotected) */
+ case NVM_COMMAND_ERASE_ROW:
+ case NVM_COMMAND_WRITE_PAGE:
+ case NVM_COMMAND_LOCK_REGION:
+ case NVM_COMMAND_UNLOCK_REGION:
+#ifdef FEATURE_NVM_RWWEE
+ case NVM_COMMAND_RWWEE_ERASE_ROW:
+ case NVM_COMMAND_RWWEE_WRITE_PAGE:
+#endif
+
+ /* Set address, command will be issued elsewhere */
+ nvm_module->ADDR.reg = (uintptr_t)&NVM_MEMORY[address / 4];
+ break;
+
+ /* Commands not requiring address */
+ case NVM_COMMAND_PAGE_BUFFER_CLEAR:
+ case NVM_COMMAND_SET_SECURITY_BIT:
+ case NVM_COMMAND_ENTER_LOW_POWER_MODE:
+ case NVM_COMMAND_EXIT_LOW_POWER_MODE:
+ break;
+
+ default:
+ /* Restore the setting */
+ nvm_module->CTRLB.reg = ctrlb_bak;
+ return STATUS_ERR_INVALID_ARG;
+ }
+
+ /* Set command */
+ nvm_module->CTRLA.reg = command | NVMCTRL_CTRLA_CMDEX_KEY;
+
+ /* Wait for the NVM controller to become ready */
+ while (!nvm_is_ready()) {
+ }
+
+ /* Restore the setting */
+ nvm_module->CTRLB.reg = ctrlb_bak;
+
+ return STATUS_OK;
+}
+
+/**
+ * \brief Updates an arbitrary section of a page with new data.
+ *
+ * Writes from a buffer to a given page in the NVM memory, retaining any
+ * unmodified data already stored in the page.
+ *
+ * \note If manual write mode is enable, the write command must be executed after
+ * this function, otherwise the data will not write to NVM from page buffer.
+ *
+ * \warning This routine is unsafe if data integrity is critical; a system reset
+ * during the update process will result in up to one row of data being
+ * lost. If corruption must be avoided in all circumstances (including
+ * power loss or system reset) this function should not be used.
+ *
+ * \param[in] destination_address Destination page address to write to
+ * \param[in] buffer Pointer to buffer where the data to write is
+ * stored
+ * \param[in] offset Number of bytes to offset the data write in
+ * the page
+ * \param[in] length Number of bytes in the page to update
+ *
+ * \return Status of the attempt to update a page.
+ *
+ * \retval STATUS_OK Requested NVM memory page was successfully
+ * read
+ * \retval STATUS_BUSY NVM controller was busy when the operation
+ * was attempted
+ * \retval STATUS_ERR_BAD_ADDRESS The requested address was outside the
+ * acceptable range of the NVM memory region
+ * \retval STATUS_ERR_INVALID_ARG The supplied length and offset was invalid
+ */
+enum status_code nvm_update_buffer(
+ const uint32_t destination_address,
+ uint8_t *const buffer,
+ uint16_t offset,
+ uint16_t length)
+{
+ enum status_code error_code = STATUS_OK;
+ uint8_t row_buffer[NVMCTRL_ROW_PAGES][NVMCTRL_PAGE_SIZE];
+
+ /* Ensure the read does not overflow the page size */
+ if ((offset + length) > _nvm_dev.page_size) {
+ return STATUS_ERR_INVALID_ARG;
+ }
+
+ /* Calculate the starting row address of the page to update */
+ uint32_t row_start_address =
+ destination_address & ~((_nvm_dev.page_size * NVMCTRL_ROW_PAGES) - 1);
+
+ /* Read in the current row contents */
+ for (uint32_t i = 0; i < NVMCTRL_ROW_PAGES; i++) {
+ do
+ {
+ error_code = nvm_read_buffer(
+ row_start_address + (i * _nvm_dev.page_size),
+ row_buffer[i], _nvm_dev.page_size);
+ } while (error_code == STATUS_BUSY);
+
+ if (error_code != STATUS_OK) {
+ return error_code;
+ }
+ }
+
+ /* Calculate the starting page in the row that is to be updated */
+ uint8_t page_in_row =
+ (destination_address % (_nvm_dev.page_size * NVMCTRL_ROW_PAGES)) /
+ _nvm_dev.page_size;
+
+ /* Update the specified bytes in the page buffer */
+ for (uint32_t i = 0; i < length; i++) {
+ row_buffer[page_in_row][offset + i] = buffer[i];
+ }
+
+ system_interrupt_enter_critical_section();
+
+ /* Erase the row */
+ do
+ {
+ error_code = nvm_erase_row(row_start_address);
+ } while (error_code == STATUS_BUSY);
+
+ if (error_code != STATUS_OK) {
+ system_interrupt_leave_critical_section();
+ return error_code;
+ }
+
+ /* Write the updated row contents to the erased row */
+ for (uint32_t i = 0; i < NVMCTRL_ROW_PAGES; i++) {
+ do
+ {
+ error_code = nvm_write_buffer(
+ row_start_address + (i * _nvm_dev.page_size),
+ row_buffer[i], _nvm_dev.page_size);
+ } while (error_code == STATUS_BUSY);
+
+ if (error_code != STATUS_OK) {
+ system_interrupt_leave_critical_section();
+ return error_code;
+ }
+ }
+
+ system_interrupt_leave_critical_section();
+
+ return error_code;
+}
+
+/**
+ * \brief Writes a number of bytes to a page in the NVM memory region.
+ *
+ * Writes from a buffer to a given page address in the NVM memory.
+ *
+ * \param[in] destination_address Destination page address to write to
+ * \param[in] buffer Pointer to buffer where the data to write is
+ * stored
+ * \param[in] length Number of bytes in the page to write
+ *
+ * \note If writing to a page that has previously been written to, the page's
+ * row should be erased (via \ref nvm_erase_row()) before attempting to
+ * write new data to the page.
+ *
+ * \note For SAM D21 RWW devices, see \c SAMD21_64K, command \c NVM_COMMAND_RWWEE_WRITE_PAGE
+ * must be executed before any other commands after writing a page,
+ * refer to errata 13588.
+ *
+ * \note If manual write mode is enabled, the write command must be executed after
+ * this function, otherwise the data will not write to NVM from page buffer.
+ *
+ * \return Status of the attempt to write a page.
+ *
+ * \retval STATUS_OK Requested NVM memory page was successfully
+ * read
+ * \retval STATUS_BUSY NVM controller was busy when the operation
+ * was attempted
+ * \retval STATUS_ERR_BAD_ADDRESS The requested address was outside the
+ * acceptable range of the NVM memory region or
+ * not aligned to the start of a page
+ * \retval STATUS_ERR_INVALID_ARG The supplied write length was invalid
+ */
+enum status_code nvm_write_buffer(
+ const uint32_t destination_address,
+ const uint8_t *buffer,
+ uint16_t length)
+{
+#ifdef FEATURE_NVM_RWWEE
+ bool is_rww_eeprom = false;
+#endif
+
+ /* Check if the destination address is valid */
+ if (destination_address >
+ ((uint32_t)_nvm_dev.page_size * _nvm_dev.number_of_pages)) {
+#ifdef FEATURE_NVM_RWWEE
+ if (destination_address >= ((uint32_t)NVMCTRL_RWW_EEPROM_SIZE + NVMCTRL_RWW_EEPROM_ADDR)
+ || destination_address < NVMCTRL_RWW_EEPROM_ADDR){
+ return STATUS_ERR_BAD_ADDRESS;
+ }
+ is_rww_eeprom = true;
+#else
+ return STATUS_ERR_BAD_ADDRESS;
+#endif
+ }
+
+ /* Check if the write address not aligned to the start of a page */
+ if (destination_address & (_nvm_dev.page_size - 1)) {
+ return STATUS_ERR_BAD_ADDRESS;
+ }
+
+ /* Check if the write length is longer than an NVM page */
+ if (length > _nvm_dev.page_size) {
+ return STATUS_ERR_INVALID_ARG;
+ }
+
+ /* Get a pointer to the module hardware instance */
+ Nvmctrl *const nvm_module = NVMCTRL;
+
+ /* Check if the module is busy */
+ if (!nvm_is_ready()) {
+ return STATUS_BUSY;
+ }
+
+ /* Erase the page buffer before buffering new data */
+ nvm_module->CTRLA.reg = NVM_COMMAND_PAGE_BUFFER_CLEAR | NVMCTRL_CTRLA_CMDEX_KEY;
+
+ /* Check if the module is busy */
+ while (!nvm_is_ready()) {
+ /* Force-wait for the buffer clear to complete */
+ }
+
+ /* Clear error flags */
+ nvm_module->STATUS.reg = NVMCTRL_STATUS_MASK;
+
+ uint32_t nvm_address = destination_address / 2;
+
+ /* NVM _must_ be accessed as a series of 16-bit words, perform manual copy
+ * to ensure alignment */
+ for (uint16_t i = 0; i < length; i += 2) {
+ uint16_t data;
+
+ /* Copy first byte of the 16-bit chunk to the temporary buffer */
+ data = buffer[i];
+
+ /* If we are not at the end of a write request with an odd byte count,
+ * store the next byte of data as well */
+ if (i < (length - 1)) {
+ data |= (buffer[i + 1] << 8);
+ }
+
+ /* Store next 16-bit chunk to the NVM memory space */
+ NVM_MEMORY[nvm_address++] = data;
+ }
+
+ /* If automatic page write mode is enable, then perform a manual NVM
+ * write when the length of data to be programmed is less than page size
+ */
+ if ((_nvm_dev.manual_page_write == false) && (length < NVMCTRL_PAGE_SIZE)) {
+#ifdef FEATURE_NVM_RWWEE
+ return ((is_rww_eeprom) ?
+ (nvm_execute_command(NVM_COMMAND_RWWEE_WRITE_PAGE,destination_address, 0)):
+ (nvm_execute_command(NVM_COMMAND_WRITE_PAGE,destination_address, 0)));
+#else
+ return nvm_execute_command(NVM_COMMAND_WRITE_PAGE,
+ destination_address, 0);
+#endif
+ }
+
+ return STATUS_OK;
+}
+
+/**
+ * \brief Reads a number of bytes from a page in the NVM memory region.
+ *
+ * Reads a given number of bytes from a given page address in the NVM memory
+ * space into a buffer.
+ *
+ * \param[in] source_address Source page address to read from
+ * \param[out] buffer Pointer to a buffer where the content of the read
+ * page will be stored
+ * \param[in] length Number of bytes in the page to read
+ *
+ * \return Status of the page read attempt.
+ *
+ * \retval STATUS_OK Requested NVM memory page was successfully
+ * read
+ * \retval STATUS_BUSY NVM controller was busy when the operation
+ * was attempted
+ * \retval STATUS_ERR_BAD_ADDRESS The requested address was outside the
+ * acceptable range of the NVM memory region or
+ * not aligned to the start of a page
+ * \retval STATUS_ERR_INVALID_ARG The supplied read length was invalid
+ */
+enum status_code nvm_read_buffer(
+ const uint32_t source_address,
+ uint8_t *const buffer,
+ uint16_t length)
+{
+ /* Check if the source address is valid */
+ if (source_address >
+ ((uint32_t)_nvm_dev.page_size * _nvm_dev.number_of_pages)) {
+#ifdef FEATURE_NVM_RWWEE
+ if (source_address >= ((uint32_t)NVMCTRL_RWW_EEPROM_SIZE + NVMCTRL_RWW_EEPROM_ADDR)
+ || source_address < NVMCTRL_RWW_EEPROM_ADDR){
+ return STATUS_ERR_BAD_ADDRESS;
+ }
+#else
+ return STATUS_ERR_BAD_ADDRESS;
+#endif
+ }
+
+ /* Check if the read address is not aligned to the start of a page */
+ if (source_address & (_nvm_dev.page_size - 1)) {
+ return STATUS_ERR_BAD_ADDRESS;
+ }
+
+ /* Check if the write length is longer than an NVM page */
+ if (length > _nvm_dev.page_size) {
+ return STATUS_ERR_INVALID_ARG;
+ }
+
+ /* Get a pointer to the module hardware instance */
+ Nvmctrl *const nvm_module = NVMCTRL;
+
+ /* Check if the module is busy */
+ if (!nvm_is_ready()) {
+ return STATUS_BUSY;
+ }
+
+ /* Clear error flags */
+ nvm_module->STATUS.reg = NVMCTRL_STATUS_MASK;
+
+ uint32_t page_address = source_address / 2;
+
+ /* NVM _must_ be accessed as a series of 16-bit words, perform manual copy
+ * to ensure alignment */
+ for (uint16_t i = 0; i < length; i += 2) {
+ /* Fetch next 16-bit chunk from the NVM memory space */
+ uint16_t data = NVM_MEMORY[page_address++];
+
+ /* Copy first byte of the 16-bit chunk to the destination buffer */
+ buffer[i] = (data & 0xFF);
+
+ /* If we are not at the end of a read request with an odd byte count,
+ * store the next byte of data as well */
+ if (i < (length - 1)) {
+ buffer[i + 1] = (data >> 8);
+ }
+ }
+
+ return STATUS_OK;
+}
+
+/**
+ * \brief Erases a row in the NVM memory space.
+ *
+ * Erases a given row in the NVM memory region.
+ *
+ * \param[in] row_address Address of the row to erase
+ *
+ * \return Status of the NVM row erase attempt.
+ *
+ * \retval STATUS_OK Requested NVM memory row was successfully
+ * erased
+ * \retval STATUS_BUSY NVM controller was busy when the operation
+ * was attempted
+ * \retval STATUS_ERR_BAD_ADDRESS The requested row address was outside the
+ * acceptable range of the NVM memory region or
+ * not aligned to the start of a row
+ * \retval STATUS_ABORTED NVM erased error
+ */
+enum status_code nvm_erase_row(
+ const uint32_t row_address)
+{
+#ifdef FEATURE_NVM_RWWEE
+ bool is_rww_eeprom = false;
+#endif
+
+ /* Check if the row address is valid */
+ if (row_address >
+ ((uint32_t)_nvm_dev.page_size * _nvm_dev.number_of_pages)) {
+#ifdef FEATURE_NVM_RWWEE
+ if (row_address >= ((uint32_t)NVMCTRL_RWW_EEPROM_SIZE + NVMCTRL_RWW_EEPROM_ADDR)
+ || row_address < NVMCTRL_RWW_EEPROM_ADDR){
+ return STATUS_ERR_BAD_ADDRESS;e4
+ }
+ is_rww_eeprom = true;
+#else
+ return STATUS_ERR_BAD_ADDRESS;
+#endif
+ }
+
+ /* Check if the address to erase is not aligned to the start of a row */
+ if (row_address & ((_nvm_dev.page_size * NVMCTRL_ROW_PAGES) - 1)) {
+ return STATUS_ERR_BAD_ADDRESS;
+ }
+
+ /* Get a pointer to the module hardware instance */
+ Nvmctrl *const nvm_module = NVMCTRL;
+
+ /* Check if the module is busy */
+ if (!nvm_is_ready()) {
+ return STATUS_BUSY;
+ }
+
+ /* Clear error flags */
+ nvm_module->STATUS.reg = NVMCTRL_STATUS_MASK;
+
+ /* Set address and command */
+ nvm_module->ADDR.reg = (uintptr_t)&NVM_MEMORY[row_address / 4];
+
+#ifdef SAMD21_64K
+ if (is_rww_eeprom) {
+ NVM_MEMORY[row_address / 2] = 0x0;
+ }
+#endif
+
+#ifdef FEATURE_NVM_RWWEE
+ nvm_module->CTRLA.reg = ((is_rww_eeprom) ?
+ (NVM_COMMAND_RWWEE_ERASE_ROW | NVMCTRL_CTRLA_CMDEX_KEY):
+ (NVM_COMMAND_ERASE_ROW | NVMCTRL_CTRLA_CMDEX_KEY));
+#else
+ nvm_module->CTRLA.reg = NVM_COMMAND_ERASE_ROW | NVMCTRL_CTRLA_CMDEX_KEY;
+#endif
+
+ while (!nvm_is_ready()) {
+ }
+
+ /* There existed error in NVM erase operation */
+ if ((enum nvm_error)(nvm_module->STATUS.reg & NVM_ERRORS_MASK) != NVM_ERROR_NONE) {
+ return STATUS_ABORTED;
+ }
+
+ return STATUS_OK;
+}
+
+/**
+ * \brief Reads the parameters of the NVM controller.
+ *
+ * Retrieves the page size, number of pages, and other configuration settings
+ * of the NVM region.
+ *
+ * \param[out] parameters Parameter structure, which holds page size and
+ * number of pages in the NVM memory
+ */
+void nvm_get_parameters(
+ struct nvm_parameters *const parameters)
+{
+ /* Sanity check parameters */
+ Assert(parameters);
+
+ /* Get a pointer to the module hardware instance */
+ Nvmctrl *const nvm_module = NVMCTRL;
+
+ /* Clear error flags */
+ nvm_module->STATUS.reg = NVMCTRL_STATUS_MASK;
+
+ /* Read out from the PARAM register */
+ uint32_t param_reg = nvm_module->PARAM.reg;
+
+ /* Mask out page size exponent and convert to a number of bytes */
+ parameters->page_size =
+ 8 << ((param_reg & NVMCTRL_PARAM_PSZ_Msk) >> NVMCTRL_PARAM_PSZ_Pos);
+
+ /* Mask out number of pages count */
+ parameters->nvm_number_of_pages =
+ (param_reg & NVMCTRL_PARAM_NVMP_Msk) >> NVMCTRL_PARAM_NVMP_Pos;
+
+#ifdef FEATURE_NVM_RWWEE
+ /* Mask out rwwee number of pages count */
+ parameters->rww_eeprom_number_of_pages =
+ (param_reg & NVMCTRL_PARAM_RWWEEP_Msk) >> NVMCTRL_PARAM_RWWEEP_Pos;
+#endif
+
+ /* Read the current EEPROM fuse value from the USER row */
+ uint16_t eeprom_fuse_value =
+ (NVM_USER_MEMORY[NVMCTRL_FUSES_EEPROM_SIZE_Pos / 16] &
+ NVMCTRL_FUSES_EEPROM_SIZE_Msk) >> NVMCTRL_FUSES_EEPROM_SIZE_Pos;
+
+ /* Translate the EEPROM fuse byte value to a number of NVM pages */
+ if (eeprom_fuse_value == 7) {
+ parameters->eeprom_number_of_pages = 0;
+ }
+ else {
+ parameters->eeprom_number_of_pages =
+ NVMCTRL_ROW_PAGES << (6 - eeprom_fuse_value);
+ }
+
+ /* Read the current BOOTSZ fuse value from the USER row */
+ uint16_t boot_fuse_value =
+ (NVM_USER_MEMORY[NVMCTRL_FUSES_BOOTPROT_Pos / 16] &
+ NVMCTRL_FUSES_BOOTPROT_Msk) >> NVMCTRL_FUSES_BOOTPROT_Pos;
+
+ /* Translate the BOOTSZ fuse byte value to a number of NVM pages */
+ if (boot_fuse_value == 7) {
+ parameters->bootloader_number_of_pages = 0;
+ }
+ else {
+ parameters->bootloader_number_of_pages =
+ NVMCTRL_ROW_PAGES << (7 - boot_fuse_value);
+ }
+}
+
+/**
+ * \brief Checks whether the page region is locked.
+ *
+ * Extracts the region to which the given page belongs and checks whether
+ * that region is locked.
+ *
+ * \param[in] page_number Page number to be checked
+ *
+ * \return Page lock status.
+ *
+ * \retval true Page is locked
+ * \retval false Page is not locked
+ *
+ */
+bool nvm_is_page_locked(uint16_t page_number)
+{
+ uint16_t pages_in_region;
+ uint16_t region_number;
+
+#ifdef FEATURE_NVM_RWWEE
+ Assert(page_number < _nvm_dev.number_of_pages);
+#endif
+
+ /* Get a pointer to the module hardware instance */
+ Nvmctrl *const nvm_module = NVMCTRL;
+
+ /* Get number of pages in a region */
+ pages_in_region = _nvm_dev.number_of_pages / 16;
+
+ /* Get region for given page */
+ region_number = page_number / pages_in_region;
+
+ return !(nvm_module->LOCK.reg & (1 << region_number));
+}
+
+///@cond INTERNAL
+
+/**
+ * \internal
+ *
+ * \brief Translate fusebit words into struct content.
+ *
+ */
+static void _nvm_translate_raw_fusebits_to_struct (
+ uint32_t *raw_user_row,
+ struct nvm_fusebits *fusebits)
+{
+
+ fusebits->bootloader_size = (enum nvm_bootloader_size)
+ ((raw_user_row[0] & NVMCTRL_FUSES_BOOTPROT_Msk)
+ >> NVMCTRL_FUSES_BOOTPROT_Pos);
+
+ fusebits->eeprom_size = (enum nvm_eeprom_emulator_size)
+ ((raw_user_row[0] & NVMCTRL_FUSES_EEPROM_SIZE_Msk)
+ >> NVMCTRL_FUSES_EEPROM_SIZE_Pos);
+
+#if (SAML21) || (SAML22) || (SAMR30)
+ fusebits->bod33_level = (uint8_t)
+ ((raw_user_row[0] & FUSES_BOD33USERLEVEL_Msk)
+ >> FUSES_BOD33USERLEVEL_Pos);
+
+ fusebits->bod33_enable = (bool)
+ (!((raw_user_row[0] & FUSES_BOD33_DIS_Msk)
+ >> FUSES_BOD33_DIS_Pos));
+
+ fusebits->bod33_action = (enum nvm_bod33_action)
+ ((raw_user_row[0] & FUSES_BOD33_ACTION_Msk)
+ >> FUSES_BOD33_ACTION_Pos);
+
+ fusebits->bod33_hysteresis = (bool)
+ ((raw_user_row[1] & FUSES_BOD33_HYST_Msk)
+ >> FUSES_BOD33_HYST_Pos);
+
+#elif (SAMD20) || (SAMD21) || (SAMR21)|| (SAMDA1) || (SAMD09) || (SAMD10)
+ fusebits->bod33_level = (uint8_t)
+ ((raw_user_row[0] & FUSES_BOD33USERLEVEL_Msk)
+ >> FUSES_BOD33USERLEVEL_Pos);
+
+ fusebits->bod33_enable = (bool)
+ ((raw_user_row[0] & FUSES_BOD33_EN_Msk)
+ >> FUSES_BOD33_EN_Pos);
+
+ fusebits->bod33_action = (enum nvm_bod33_action)
+ ((raw_user_row[0] & FUSES_BOD33_ACTION_Msk)
+ >> FUSES_BOD33_ACTION_Pos);
+ fusebits->bod33_hysteresis = (bool)
+ ((raw_user_row[1] & FUSES_BOD33_HYST_Msk)
+ >> FUSES_BOD33_HYST_Pos);
+#elif (SAMC20) || (SAMC21)
+ fusebits->bodvdd_level = (uint8_t)
+ ((raw_user_row[0] & FUSES_BODVDDUSERLEVEL_Msk)
+ >> FUSES_BODVDDUSERLEVEL_Pos);
+
+ fusebits->bodvdd_enable = (bool)
+ (!((raw_user_row[0] & FUSES_BODVDD_DIS_Msk)
+ >> FUSES_BODVDD_DIS_Pos));
+
+ fusebits->bodvdd_action = (enum nvm_bod33_action)
+ ((raw_user_row[0] & FUSES_BODVDD_ACTION_Msk)
+ >> FUSES_BODVDD_ACTION_Pos);
+
+ fusebits->bodvdd_hysteresis = (raw_user_row[1] & FUSES_BODVDD_HYST_Msk)
+ >> FUSES_BODVDD_HYST_Pos;
+#else
+ fusebits->bod33_level = (uint8_t)
+ ((raw_user_row[0] & SYSCTRL_FUSES_BOD33USERLEVEL_Msk)
+ >> SYSCTRL_FUSES_BOD33USERLEVEL_Pos);
+
+ fusebits->bod33_enable = (bool)
+ ((raw_user_row[0] & SYSCTRL_FUSES_BOD33_EN_Msk)
+ >> SYSCTRL_FUSES_BOD33_EN_Pos);
+
+ fusebits->bod33_action = (enum nvm_bod33_action)
+ ((raw_user_row[0] & SYSCTRL_FUSES_BOD33_ACTION_Msk)
+ >> SYSCTRL_FUSES_BOD33_ACTION_Pos);
+
+ fusebits->bod33_hysteresis = (bool)
+ ((raw_user_row[1] & SYSCTRL_FUSES_BOD33_HYST_Msk)
+ >> SYSCTRL_FUSES_BOD33_HYST_Pos);
+
+#endif
+
+#ifdef FEATURE_BOD12
+
+#ifndef FUSES_BOD12USERLEVEL_Pos
+#define FUSES_BOD12USERLEVEL_Pos 17
+#define FUSES_BOD12USERLEVEL_Msk (0x3Ful << FUSES_BOD12USERLEVEL_Pos)
+#endif
+#ifndef FUSES_BOD12_DIS_Pos
+#define FUSES_BOD12_DIS_Pos 23
+#define FUSES_BOD12_DIS_Msk (0x1ul << FUSES_BOD12_DIS_Pos)
+#endif
+#ifndef FUSES_BOD12_ACTION_Pos
+#define FUSES_BOD12_ACTION_Pos 24
+#define FUSES_BOD12_ACTION_Msk (0x3ul << FUSES_BOD12_ACTION_Pos)
+#endif
+
+ fusebits->bod12_level = (uint8_t)
+ ((raw_user_row[0] & FUSES_BOD12USERLEVEL_Msk)
+ >> FUSES_BOD12USERLEVEL_Pos);
+
+ fusebits->bod12_enable = (bool)
+ (!((raw_user_row[0] & FUSES_BOD12_DIS_Msk)
+ >> FUSES_BOD12_DIS_Pos));
+
+ fusebits->bod12_action = (enum nvm_bod12_action)
+ ((raw_user_row[0] & FUSES_BOD12_ACTION_Msk)
+ >> FUSES_BOD33_ACTION_Pos);
+
+ fusebits->bod12_hysteresis = (bool)
+ ((raw_user_row[1] & FUSES_BOD12_HYST_Msk)
+ >> FUSES_BOD12_HYST_Pos);
+#endif
+
+ fusebits->wdt_enable = (bool)
+ ((raw_user_row[0] & WDT_FUSES_ENABLE_Msk) >> WDT_FUSES_ENABLE_Pos);
+
+ fusebits->wdt_always_on = (bool)
+ ((raw_user_row[0] & WDT_FUSES_ALWAYSON_Msk) >> WDT_FUSES_ALWAYSON_Pos);
+
+ fusebits->wdt_timeout_period = (uint8_t)
+ ((raw_user_row[0] & WDT_FUSES_PER_Msk) >> WDT_FUSES_PER_Pos);
+
+#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30)
+ fusebits->wdt_window_timeout = (enum nvm_wdt_window_timeout)
+ ((raw_user_row[1] & WDT_FUSES_WINDOW_Msk) >> WDT_FUSES_WINDOW_Pos);
+#else
+ /* WDT Windows timout lay between two 32-bit words in the user row. Because only one bit lays in word[0],
+ bits in word[1] must be left sifted by one to make the correct number */
+ fusebits->wdt_window_timeout = (enum nvm_wdt_window_timeout)
+ (((raw_user_row[0] & WDT_FUSES_WINDOW_0_Msk) >> WDT_FUSES_WINDOW_0_Pos) |
+ ((raw_user_row[1] & WDT_FUSES_WINDOW_1_Msk) << 1));
+#endif
+ fusebits->wdt_early_warning_offset = (enum nvm_wdt_early_warning_offset)
+ ((raw_user_row[1] & WDT_FUSES_EWOFFSET_Msk) >> WDT_FUSES_EWOFFSET_Pos);
+
+ fusebits->wdt_window_mode_enable_at_poweron = (bool)
+ ((raw_user_row[1] & WDT_FUSES_WEN_Msk) >> WDT_FUSES_WEN_Pos);
+
+ fusebits->lockbits = (uint16_t)
+ ((raw_user_row[1] & NVMCTRL_FUSES_REGION_LOCKS_Msk)
+ >> NVMCTRL_FUSES_REGION_LOCKS_Pos);
+
+}
+
+///@endcond
+
+/**
+ * \brief Get fuses from user row.
+ *
+ * Read out the fuse settings from the user row.
+ *
+ * \param[in] fusebits Pointer to a 64-bit wide memory buffer of type struct nvm_fusebits
+ *
+ * \return Status of read fuses attempt.
+ *
+ * \retval STATUS_OK This function will always return STATUS_OK
+ */
+enum status_code nvm_get_fuses (
+ struct nvm_fusebits *fusebits)
+{
+ enum status_code error_code = STATUS_OK;
+ uint32_t raw_fusebits[2];
+
+ /* Make sure the module is ready */
+ while (!nvm_is_ready()) {
+ }
+
+ /* Read the fuse settings in the user row, 64 bit */
+ raw_fusebits[0] = ((uint32_t)(NVM_MEMORY[NVMCTRL_USER / 2] & 0xffff) << 16) +
+ (uint32_t)(NVM_MEMORY[(NVMCTRL_USER / 2) + 1] & 0xffff);
+ raw_fusebits[1] = ((uint32_t)(NVM_MEMORY[(NVMCTRL_USER / 2) + 2] & 0xffff) << 16) +
+ (uint32_t)(NVM_MEMORY[(NVMCTRL_USER / 2) + 3] & 0xffff);
+
+ _nvm_translate_raw_fusebits_to_struct(raw_fusebits, fusebits);
+
+ return error_code;
+}
+
+/**
+ * \brief Set fuses from user row.
+ *
+ * Set fuse settings from the user row.
+ *
+ * \note When writing to the user row, the values do not get loaded by the
+ * other modules on the device until a device reset occurs.
+ *
+ * \param[in] fusebits Pointer to a 64-bit wide memory buffer of type struct nvm_fusebits
+ *
+ * \return Status of read fuses attempt.
+ *
+ * \retval STATUS_OK This function will always return STATUS_OK
+ *
+ * \retval STATUS_BUSY If the NVM controller was already busy
+ * executing a command when the new command
+ * was issued
+ * \retval STATUS_ERR_IO If the command was invalid due to memory or
+ * security locking
+ * \retval STATUS_ERR_INVALID_ARG If the given command was invalid or
+ * unsupported
+ * \retval STATUS_ERR_BAD_ADDRESS If the given address was invalid
+ */
+
+enum status_code nvm_set_fuses(struct nvm_fusebits *fb)
+{
+ uint32_t fusebits[2];
+ enum status_code error_code = STATUS_OK;
+
+ if (fb == NULL) {
+ return STATUS_ERR_INVALID_ARG;
+ }
+ /* Read the fuse settings in the user row, 64 bit */
+ fusebits[0] = *((uint32_t *)NVMCTRL_AUX0_ADDRESS);
+ fusebits[1] = *(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1);
+
+ /* Set user fuses bit */
+ fusebits[0] &= (~NVMCTRL_FUSES_BOOTPROT_Msk);
+ fusebits[0] |= NVMCTRL_FUSES_BOOTPROT(fb->bootloader_size);
+
+ fusebits[0] &= (~NVMCTRL_FUSES_EEPROM_SIZE_Msk);
+ fusebits[0] |= NVMCTRL_FUSES_EEPROM_SIZE(fb->eeprom_size);
+
+#if (SAML21) || (SAML22) || (SAMR30)
+ fusebits[0] &= (~FUSES_BOD33USERLEVEL_Msk);
+ fusebits[0] |= FUSES_BOD33USERLEVEL(fb->bod33_level);
+
+ fusebits[0] &= (~FUSES_BOD33_DIS_Msk);
+ fusebits[0] |= (!fb->bod33_enable) << FUSES_BOD33_DIS_Pos;
+
+ fusebits[0] &= (~FUSES_BOD33_ACTION_Msk);
+ fusebits[0] |= fb->bod33_action << FUSES_BOD33_ACTION_Pos;
+
+ fusebits[1] &= (~FUSES_BOD33_HYST_Msk);
+ fusebits[1] |= fb->bod33_hysteresis << FUSES_BOD33_HYST_Pos;
+
+#elif (SAMD20) || (SAMD21) || (SAMR21) || (SAMDA1) || (SAMD09) || (SAMD10)
+ fusebits[0] &= (~FUSES_BOD33USERLEVEL_Msk);
+ fusebits[0] |= FUSES_BOD33USERLEVEL(fb->bod33_level);
+
+ fusebits[0] &= (~FUSES_BOD33_EN_Msk);
+ fusebits[0] |= (fb->bod33_enable) << FUSES_BOD33_EN_Pos;
+
+ fusebits[0] &= (~FUSES_BOD33_ACTION_Msk);
+ fusebits[0] |= fb->bod33_action << FUSES_BOD33_ACTION_Pos;
+
+ fusebits[1] &= (~FUSES_BOD33_HYST_Msk);
+ fusebits[1] |= fb->bod33_hysteresis << FUSES_BOD33_HYST_Pos;
+
+#elif (SAMC20) || (SAMC21)
+ fusebits[0] &= (~FUSES_BODVDDUSERLEVEL_Msk);
+ fusebits[0] |= FUSES_BODVDDUSERLEVEL(fb->bodvdd_level);
+
+ fusebits[0] &= (~FUSES_BODVDD_DIS_Msk);
+ fusebits[0] |= (!fb->bodvdd_enable) << FUSES_BODVDD_DIS_Pos;
+
+ fusebits[0] &= (~FUSES_BODVDD_ACTION_Msk);
+ fusebits[0] |= fb->bodvdd_action << FUSES_BODVDD_ACTION_Pos;
+
+ fusebits[1] &= (~FUSES_BODVDD_HYST_Msk);
+ fusebits[1] |= fb->bodvdd_hysteresis << FUSES_BODVDD_HYST_Pos;
+
+#else
+ fusebits[0] &= (~SYSCTRL_FUSES_BOD33USERLEVEL_Msk);
+ fusebits[0] |= SYSCTRL_FUSES_BOD33USERLEVEL(fb->bod33_level);
+
+ fusebits[0] &= (~SYSCTRL_FUSES_BOD33_EN_Msk);
+ fusebits[0] |= (fb->bod33_enable) << SYSCTRL_FUSES_BOD33_EN_Pos;
+
+ fusebits[0] &= (~SYSCTRL_FUSES_BOD33_ACTION_Msk);
+ fusebits[0] |= fb->bod33_action << SYSCTRL_FUSES_BOD33_ACTION_Pos;
+
+ fusebits[1] &= (~SYSCTRL_FUSES_BOD33_HYST_Msk);
+ fusebits[1] |= fb->bod33_hysteresis << SYSCTRL_FUSES_BOD33_HYST_Pos;
+
+#endif
+
+ fusebits[0] &= (~WDT_FUSES_ENABLE_Msk);
+ fusebits[0] |= fb->wdt_enable << WDT_FUSES_ENABLE_Pos;
+
+ fusebits[0] &= (~WDT_FUSES_ALWAYSON_Msk);
+ fusebits[0] |= (fb->wdt_always_on) << WDT_FUSES_ALWAYSON_Pos;
+
+ fusebits[0] &= (~WDT_FUSES_PER_Msk);
+ fusebits[0] |= fb->wdt_timeout_period << WDT_FUSES_PER_Pos;
+
+#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30)
+ fusebits[1] &= (~WDT_FUSES_WINDOW_Msk);
+ fusebits[1] |= fb->wdt_window_timeout << WDT_FUSES_WINDOW_Pos;
+#else
+ /* WDT Windows timout lay between two 32-bit words in the user row. the last one bit lays in word[0],
+ and the other bits in word[1] */
+ fusebits[0] &= (~WDT_FUSES_WINDOW_0_Msk);
+ fusebits[0] |= (fb->wdt_window_timeout & 0x1) << WDT_FUSES_WINDOW_0_Pos;
+
+ fusebits[1] &= (~WDT_FUSES_WINDOW_1_Msk);
+ fusebits[1] |= (fb->wdt_window_timeout >> 1) << WDT_FUSES_WINDOW_1_Pos;
+
+#endif
+ fusebits[1] &= (~WDT_FUSES_EWOFFSET_Msk);
+ fusebits[1] |= fb->wdt_early_warning_offset << WDT_FUSES_EWOFFSET_Pos;
+
+ fusebits[1] &= (~WDT_FUSES_WEN_Msk);
+ fusebits[1] |= fb->wdt_window_mode_enable_at_poweron << WDT_FUSES_WEN_Pos;
+
+ fusebits[1] &= (~NVMCTRL_FUSES_REGION_LOCKS_Msk);
+ fusebits[1] |= fb->lockbits << NVMCTRL_FUSES_REGION_LOCKS_Pos;
+
+#ifdef FEATURE_BOD12
+
+#ifndef FUSES_BOD12USERLEVEL_Pos
+#define FUSES_BOD12USERLEVEL_Pos 17
+#define FUSES_BOD12USERLEVEL_Msk (0x3Ful << FUSES_BOD12USERLEVEL_Pos)
+#endif
+#ifndef FUSES_BOD12_DIS_Pos
+#define FUSES_BOD12_DIS_Pos 23
+#define FUSES_BOD12_DIS_Msk (0x1ul << FUSES_BOD12_DIS_Pos)
+#endif
+#ifndef FUSES_BOD12_ACTION_Pos
+#define FUSES_BOD12_ACTION_Pos 24
+#define FUSES_BOD12_ACTION_Msk (0x3ul << FUSES_BOD12_ACTION_Pos)
+#endif
+
+ fusebits[0] &= (~FUSES_BOD12USERLEVEL_Msk);
+ fusebits[0] |= ((FUSES_BOD12USERLEVEL_Msk & ((fb->bod12_level) <<
+ FUSES_BOD12USERLEVEL_Pos)));
+
+ fusebits[0] &= (~FUSES_BOD12_DIS_Msk);
+ fusebits[0] |= (!fb->bod12_enable) << FUSES_BOD12_DIS_Pos;
+
+ fusebits[0] &= (~FUSES_BOD12_ACTION_Msk);
+ fusebits[0] |= fb->bod12_action << FUSES_BOD12_ACTION_Pos;
+
+ fusebits[1] &= (~FUSES_BOD12_HYST_Msk);
+ fusebits[1] |= fb->bod12_hysteresis << FUSES_BOD12_HYST_Pos;
+#endif
+
+ error_code = nvm_execute_command(NVM_COMMAND_ERASE_AUX_ROW,NVMCTRL_AUX0_ADDRESS,0);
+ if (error_code != STATUS_OK) {
+ return error_code;
+ }
+
+ error_code = nvm_execute_command(NVM_COMMAND_PAGE_BUFFER_CLEAR,NVMCTRL_AUX0_ADDRESS,0);
+ if (error_code != STATUS_OK) {
+ return error_code;
+ }
+
+ *((uint32_t *)NVMCTRL_AUX0_ADDRESS) = fusebits[0];
+ *(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1) = fusebits[1];
+
+ error_code = nvm_execute_command(NVM_COMMAND_WRITE_AUX_ROW,NVMCTRL_AUX0_ADDRESS,0);
+ if (error_code != STATUS_OK) {
+ return error_code;
+ }
+
+ return error_code;
+}
diff --git a/atmel-samd/asf/sam0/drivers/nvm/nvm.h b/atmel-samd/asf/sam0/drivers/nvm/nvm.h
new file mode 100644
index 000000000..99245dd4a
--- /dev/null
+++ b/atmel-samd/asf/sam0/drivers/nvm/nvm.h
@@ -0,0 +1,944 @@
+/**
+ * \file
+ *
+ * \brief SAM Non-Volatile Memory driver
+ *
+ * Copyright (C) 2012-2016 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
+ *
+ */
+/*
+ * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
+ */
+#ifndef NVM_H_INCLUDED
+#define NVM_H_INCLUDED
+
+/**
+ * \defgroup asfdoc_sam0_nvm_group SAM Non-Volatile Memory (NVM) Driver
+ *
+ * This driver for Atmel&reg; | SMART ARM&reg;-based microcontrollers provides
+ * an interface for the configuration and management of non-volatile memories
+ * within the device, for partitioning, erasing, reading, and writing of data.
+ *
+ * The following peripheral is used by this module:
+ * - NVM (Non-Volatile Memory)
+ *
+ * The following devices can use this module:
+ * - Atmel | SMART SAM D20/D21
+ * - Atmel | SMART SAM R21
+ * - Atmel | SMART SAM D09/D10/D11
+ * - Atmel | SMART SAM L21/L22
+ * - Atmel | SMART SAM DA1
+ * - Atmel | SMART SAM C20/C21
+ * - Atmel | SMART SAM R30
+ *
+ * The outline of this documentation is as follows:
+ * - \ref asfdoc_sam0_nvm_prerequisites
+ * - \ref asfdoc_sam0_nvm_module_overview
+ * - \ref asfdoc_sam0_nvm_special_considerations
+ * - \ref asfdoc_sam0_nvm_extra_info
+ * - \ref asfdoc_sam0_nvm_examples
+ * - \ref asfdoc_sam0_nvm_api_overview
+ *
+ *
+ * \section asfdoc_sam0_nvm_prerequisites Prerequisites
+ *
+ * There are no prerequisites for this module.
+ *
+ *
+ * \section asfdoc_sam0_nvm_module_overview Module Overview
+ *
+ * The Non-Volatile Memory (NVM) module provides an interface to the device's
+ * Non-Volatile Memory controller, so that memory pages can be written, read,
+ * erased, and reconfigured in a standardized manner.
+ *
+ * \subsection asfdoc_sam0_nvm_features Driver Feature Macro Definition
+ * <table>
+ * <tr>
+ * <th>Driver feature macro</th>
+ * <th>Supported devices</th>
+ * </tr>
+ * <tr>
+ * <td>FEATURE_NVM_RWWEE</td>
+ * <td>SAM L21/L22, SAM D21-64K, SAM DA1, SAM C20/C21, SAM R30</td>
+ * </tr>
+ * <tr>
+ * <td>FEATURE_BOD12</td>
+ * <td>SAM L21, SAMR30</td>
+ * </tr>
+ * </table>
+ * \note The specific features are only available in the driver when the
+ * selected device supports those features.
+ *
+ * \subsection asfdoc_sam0_nvm_module_overview_regions Memory Regions
+ * The NVM memory space of the SAM devices is divided into two sections:
+ * a Main Array section, and an Auxiliary space section. The Main Array space
+ * can be configured to have an (emulated) EEPROM and/or boot loader section.
+ * The memory layout with the EEPROM and bootloader partitions is shown in
+ * \ref asfdoc_sam0_nvm_module_mem_layout "the figure below".
+ *
+ * \anchor asfdoc_sam0_nvm_module_mem_layout
+ * \dot
+ * digraph memory_layout {
+ * size="5,5"
+ * node [shape=plaintext, fontname=arial]
+ * memory [label=<
+ * <table border="0" cellborder="1" cellspacing="0" >
+ * <tr>
+ * <td align="right" border="0"> End of NVM Memory </td>
+ * <td rowspan="3" align="center"> Reserved EEPROM Section </td>
+ * </tr>
+ * <tr>
+ * <td align="right" border="0"> </td>
+ * </tr>
+ * <tr>
+ * <td align="right" border="0"> Start of EEPROM Memory </td>
+ * </tr>
+ * <tr>
+ * <td align="right" border="0"> End of Application Memory </td>
+ * <td rowspan="3" align="center"> Application Section </td>
+ * </tr>
+ * <tr>
+ * <td height="300" align="right" border="0"> </td>
+ * </tr>
+ * <tr>
+ * <td align="right" border="0"> Start of Application Memory </td>
+ * </tr>
+ * <tr>
+ * <td align="right" border="0"> End of Bootloader Memory </td>
+ * <td rowspan="3" align="center"> BOOT Section </td>
+ * </tr>
+ * <tr>
+ * <td align="right" border="0"> </td>
+ * </tr>
+ * <tr>
+ * <td align="right" border="0"> Start of NVM Memory</td>
+ * </tr>
+ * </table>
+ * >]
+ * }
+ * \enddot
+ *
+ * The Main Array is divided into rows and pages, where each row contains four
+ * pages. The size of each page may vary from 8-1024 bytes dependent of the
+ * device. Device specific parameters such as the page size and total number of
+ * pages in the NVM memory space are available via the \ref nvm_get_parameters()
+ * function.
+ *
+ * An NVM page number and address can be computed via the following equations:
+ *
+ * \f[ PageNum = (RowNum \times 4) + PagePosInRow \f]
+ * \f[ PageAddr = PageNum \times PageSize \f]
+ *
+ * \ref asfdoc_sam0_nvm_module_row_layout "The figure below" shows an example
+ * of the memory page and address values associated with logical row 7 of the
+ * NVM memory space.
+ *
+ * \anchor asfdoc_sam0_nvm_module_row_layout
+ * \dot
+ * digraph row_layout {
+ * size="4,4"
+ * node [shape=plaintext, fontname=arial]
+ * row [label=<
+ * <table border="0" cellborder="1" cellspacing="0">
+ * <tr>
+ * <td align="right" border ="0"> Row 0x07 </td>
+ * <td > Page 0x1F </td>
+ * <td > Page 0x1E </td>
+ * <td > Page 0x1D </td>
+ * <td > Page 0x1C </td>
+ * </tr>
+ * <tr>
+ * <td align="right" border ="0"> Address </td>
+ * <td border="0"> 0x7C0 </td>
+ * <td border="0"> 0x780 </td>
+ * <td border="0"> 0x740 </td>
+ * <td border="0"> 0x700 </td>
+ * </tr>
+ * </table>
+ * >]
+ * }
+ * \enddot
+ *
+ * \subsection asfdoc_sam0_nvm_module_overview_locking_regions Region Lock Bits
+ * As mentioned in \ref asfdoc_sam0_nvm_module_overview_regions, the main
+ * block of the NVM memory is divided into a number of individually addressable
+ * pages. These pages are grouped into 16 equal sized regions, where each region
+ * can be locked separately issuing an \ref NVM_COMMAND_LOCK_REGION command or
+ * by writing the LOCK bits in the User Row. Rows reserved for the EEPROM
+ * section are not affected by the lock bits or commands.
+ *
+ * \note By using the \ref NVM_COMMAND_LOCK_REGION or
+ * \ref NVM_COMMAND_UNLOCK_REGION commands the settings will remain in
+ * effect until the next device reset. By changing the default lock
+ * setting for the regions, the auxiliary space must to be written,
+ * however the adjusted configuration will not take effect until the next
+ * device reset.
+ *
+ * \note If the \ref asfdoc_sam0_nvm_special_consideration_security_bit is
+ * set, the auxiliary space cannot be written to. Clearing of the security
+ * bit can only be performed by a full chip erase.
+ *
+ * \subsection asfdoc_sam0_nvm_module_overview_sub_rw Read/Write
+ * Reading from the NVM memory can be performed using direct addressing into the
+ * NVM memory space, or by calling the \ref nvm_read_buffer() function.
+ *
+ * Writing to the NVM memory must be performed by the \ref nvm_write_buffer()
+ * function - additionally, a manual page program command must be issued if
+ * the NVM controller is configured in manual page writing mode.
+ *
+ * Before a page can be updated, the associated NVM memory row must be erased
+ * first via the \ref nvm_erase_row() function. Writing to a non-erased page
+ * will result in corrupt data being stored in the NVM memory space.
+ *
+ * \section asfdoc_sam0_nvm_special_considerations Special Considerations
+ *
+ * \subsection asfdoc_sam0_nvm_special_consideration_pageerase Page Erasure
+ * The granularity of an erase is per row, while the granularity of a write is
+ * per page. Thus, if the user application is modifying only one page of a row,
+ * the remaining pages in the row must be buffered and the row erased, as an
+ * erase is mandatory before writing to a page.
+ *
+ * \subsection asfdoc_sam0_nvm_special_consideration_clocks Clocks
+ * The user must ensure that the driver is configured with a proper number of
+ * wait states when the CPU is running at high frequencies.
+ *
+ * \subsection asfdoc_sam0_nvm_special_consideration_security_bit Security Bit
+ * The User Row in the Auxiliary Space cannot be read or written when
+ * the Security Bit is set. The Security Bit can be set by using passing
+ * \ref NVM_COMMAND_SET_SECURITY_BIT to the \ref nvm_execute_command() function,
+ * or it will be set if one tries to access a locked region. See
+ * \ref asfdoc_sam0_nvm_module_overview_locking_regions.
+ *
+ * The Security Bit can only be cleared by performing a chip erase.
+ *
+ *
+ * \section asfdoc_sam0_nvm_extra_info Extra Information
+ *
+ * For extra information, see \ref asfdoc_sam0_nvm_extra. This includes:
+ * - \ref asfdoc_sam0_nvm_extra_acronyms
+ * - \ref asfdoc_sam0_nvm_extra_dependencies
+ * - \ref asfdoc_sam0_nvm_extra_errata
+ * - \ref asfdoc_sam0_nvm_extra_history
+ *
+ *
+ * \section asfdoc_sam0_nvm_examples Examples
+ *
+ * For a list of examples related to this driver, see
+ * \ref asfdoc_sam0_nvm_exqsg.
+ *
+ *
+ * \section asfdoc_sam0_nvm_api_overview API Overview
+ * @{
+ */
+
+#include <compiler.h>
+#include <status_codes.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Define SAMD21-64K devices */
+#if defined(SAMD21E15L) || defined(SAMD21E16L) || defined(__SAMD21E15L__) || defined(__SAMD21E16L__) \
+ || defined(SAMD21E15B) || defined(SAMD21E16B) || defined(__SAMD21E15B__) || defined(__SAMD21E16B__) \
+ || defined(SAMD21E15BU) || defined(SAMD21E16BU) || defined(__SAMD21E15BU__) || defined(__SAMD21E16BU__) \
+ || defined(SAMD21G15L) || defined(SAMD21G16L) || defined(__SAMD21G15L__) || defined(__SAMD21G16L__) \
+ || defined(SAMD21G15B) || defined(SAMD21G16B) || defined(__SAMD21G15B__) || defined(__SAMD21G16B__) \
+ || defined(SAMD21J15B) || defined(SAMD21J16B) || defined(__SAMD21J15B__) || defined(__SAMD21J16B__)
+
+# define SAMD21_64K
+
+#endif
+
+/**
+ * \name Driver Feature Definition
+ *
+ * Define NVM features set according to the different device families.
+ * @{
+*/
+#if (SAML21) || (SAML22) || (SAMDA1) || (SAMC20) || (SAMC21) || (SAMR30) || defined(SAMD21_64K) || defined(__DOXYGEN__)
+/** Read while write EEPROM emulation feature. */
+# define FEATURE_NVM_RWWEE
+#endif
+#if (SAML21) || (SAMR30) || defined(__DOXYGEN__)
+/** Brown-out detector internal to the voltage regulator for VDDCORE. */
+#define FEATURE_BOD12
+#endif
+/*@}*/
+
+#if !defined(__DOXYGEN__)
+/**
+ * \brief Mask for the error flags in the status register.
+ */
+# define NVM_ERRORS_MASK (NVMCTRL_STATUS_PROGE | \
+ NVMCTRL_STATUS_LOCKE | \
+ NVMCTRL_STATUS_NVME)
+#endif
+
+/**
+ * \brief NVM error flags.
+ *
+ * Possible NVM controller error codes, which can be returned by the NVM
+ * controller after a command is issued.
+ */
+enum nvm_error {
+ /** No errors */
+ NVM_ERROR_NONE = 0,
+ /** Lock error, a locked region was attempted accessed */
+ NVM_ERROR_LOCK = NVMCTRL_STATUS_NVME | NVMCTRL_STATUS_LOCKE,
+ /** Program error, invalid command was executed */
+ NVM_ERROR_PROG = NVMCTRL_STATUS_NVME | NVMCTRL_STATUS_PROGE,
+};
+
+/**
+ * \brief NVM controller commands.
+ */
+enum nvm_command {
+ /** Erases the addressed memory row */
+ NVM_COMMAND_ERASE_ROW = NVMCTRL_CTRLA_CMD_ER,
+
+ /** Write the contents of the page buffer to the addressed memory page */
+ NVM_COMMAND_WRITE_PAGE = NVMCTRL_CTRLA_CMD_WP,
+
+ /** Erases the addressed auxiliary memory row.
+ *
+ * \note This command can only be given when the security bit is not set.
+ */
+ NVM_COMMAND_ERASE_AUX_ROW = NVMCTRL_CTRLA_CMD_EAR,
+
+ /** Write the contents of the page buffer to the addressed auxiliary memory
+ * row.
+ *
+ * \note This command can only be given when the security bit is not set.
+ */
+ NVM_COMMAND_WRITE_AUX_ROW = NVMCTRL_CTRLA_CMD_WAP,
+
+ /** Locks the addressed memory region, preventing further modifications
+ * until the region is unlocked or the device is erased
+ */
+ NVM_COMMAND_LOCK_REGION = NVMCTRL_CTRLA_CMD_LR,
+
+ /** Unlocks the addressed memory region, allowing the region contents to be
+ * modified
+ */
+ NVM_COMMAND_UNLOCK_REGION = NVMCTRL_CTRLA_CMD_UR,
+
+ /** Clears the page buffer of the NVM controller, resetting the contents to
+ * all zero values
+ */
+ NVM_COMMAND_PAGE_BUFFER_CLEAR = NVMCTRL_CTRLA_CMD_PBC,
+
+ /** Sets the device security bit, disallowing the changing of lock bits and
+ * auxiliary row data until a chip erase has been performed
+ */
+ NVM_COMMAND_SET_SECURITY_BIT = NVMCTRL_CTRLA_CMD_SSB,
+
+ /** Enter power reduction mode in the NVM controller to reduce the power
+ * consumption of the system
+ */
+ NVM_COMMAND_ENTER_LOW_POWER_MODE = NVMCTRL_CTRLA_CMD_SPRM,
+
+ /** Exit power reduction mode in the NVM controller to allow other NVM
+ * commands to be issued
+ */
+ NVM_COMMAND_EXIT_LOW_POWER_MODE = NVMCTRL_CTRLA_CMD_CPRM,
+#ifdef FEATURE_NVM_RWWEE
+ /** Read while write (RWW) EEPROM area erase row */
+ NVM_COMMAND_RWWEE_ERASE_ROW = NVMCTRL_CTRLA_CMD_RWWEEER,
+ /** RWW EEPROM write page */
+ NVM_COMMAND_RWWEE_WRITE_PAGE = NVMCTRL_CTRLA_CMD_RWWEEWP,
+#endif
+};
+
+/**
+ * \brief NVM controller power reduction mode configurations.
+ *
+ * Power reduction modes of the NVM controller, to conserve power while the
+ * device is in sleep.
+ */
+enum nvm_sleep_power_mode {
+ /** NVM controller exits low-power mode on first access after sleep */
+ NVM_SLEEP_POWER_MODE_WAKEONACCESS = NVMCTRL_CTRLB_SLEEPPRM_WAKEONACCESS_Val,
+ /** NVM controller exits low-power mode when the device exits sleep mode */
+ NVM_SLEEP_POWER_MODE_WAKEUPINSTANT = NVMCTRL_CTRLB_SLEEPPRM_WAKEUPINSTANT_Val,
+ /** Power reduction mode in the NVM controller disabled */
+ NVM_SLEEP_POWER_MODE_ALWAYS_AWAKE = NVMCTRL_CTRLB_SLEEPPRM_DISABLED_Val,
+};
+
+/**
+ * \brief NVM controller cache readmode configuration.
+ *
+ * Control how the NVM cache prefetch data from flash.
+ *
+ */
+enum nvm_cache_readmode {
+ /** The NVM Controller (cache system) does not insert wait states on
+ * a cache miss. Gives the best system performance.
+ */
+ NVM_CACHE_READMODE_NO_MISS_PENALTY,
+ /** Reduces power consumption of the cache system, but inserts a
+ * wait state each time there is a cache miss
+ */
+ NVM_CACHE_READMODE_LOW_POWER,
+ /** The cache system ensures that a cache hit or miss takes the same
+ * amount of time, determined by the number of programmed flash
+ * wait states
+ */
+ NVM_CACHE_READMODE_DETERMINISTIC,
+};
+
+/**
+ * \brief NVM controller configuration structure.
+ *
+ * Configuration structure for the NVM controller within the device.
+ */
+struct nvm_config {
+ /** Power reduction mode during device sleep */
+ enum nvm_sleep_power_mode sleep_power_mode;
+ /** Manual write mode; if enabled, pages loaded into the NVM buffer will
+ * not be written until a separate write command is issued. If disabled,
+ * writing to the last byte in the NVM page buffer will trigger an automatic
+ * write.
+ *
+ * \note If a partial page is to be written, a manual write command must be
+ * executed in either mode.
+ */
+ bool manual_page_write;
+ /** Number of wait states to insert when reading from flash, to prevent
+ * invalid data from being read at high clock frequencies
+ */
+ uint8_t wait_states;
+
+ /**
+ * Setting this to true will disable the pre-fetch cache in front of the
+ * NVM controller
+ */
+ bool disable_cache;
+#if (SAMC20) || (SAMC21)
+ /**
+ * Setting this to true will disable the pre-fetch RWW cache in front of the
+ * NVM controller.
+ * If RWW cache is enabled, NVM cache will also be enabled.
+ */
+ bool disable_rww_cache;
+#endif
+ /**
+ * Select the mode for how the cache will pre-fetch data from the flash
+ */
+ enum nvm_cache_readmode cache_readmode;
+};
+
+/**
+ * \brief NVM memory parameter structure.
+ *
+ * Structure containing the memory layout parameters of the NVM module.
+ */
+struct nvm_parameters {
+ /** Number of bytes per page */
+ uint8_t page_size;
+ /** Number of pages in the main array */
+ uint16_t nvm_number_of_pages;
+ /** Size of the emulated EEPROM memory section configured in the NVM
+ * auxiliary memory space */
+ uint32_t eeprom_number_of_pages;
+ /** Size of the Bootloader memory section configured in the NVM auxiliary
+ * memory space */
+ uint32_t bootloader_number_of_pages;
+#ifdef FEATURE_NVM_RWWEE
+ /** Number of pages in read while write EEPROM (RWWEE) emulation area */
+ uint16_t rww_eeprom_number_of_pages;
+#endif
+};
+
+/**
+ * \brief Bootloader size.
+ *
+ * Available bootloader protection sizes in kilobytes.
+ *
+ */
+enum nvm_bootloader_size {
+ /** Boot Loader Size is 32768 bytes */
+ NVM_BOOTLOADER_SIZE_128,
+ /** Boot Loader Size is 16384 bytes */
+ NVM_BOOTLOADER_SIZE_64,
+ /** Boot Loader Size is 8192 bytes */
+ NVM_BOOTLOADER_SIZE_32,
+ /** Boot Loader Size is 4096 bytes */
+ NVM_BOOTLOADER_SIZE_16,
+ /** Boot Loader Size is 2048 bytes */
+ NVM_BOOTLOADER_SIZE_8,
+ /** Boot Loader Size is 1024 bytes */
+ NVM_BOOTLOADER_SIZE_4,
+ /** Boot Loader Size is 512 bytes */
+ NVM_BOOTLOADER_SIZE_2,
+ /** Boot Loader Size is 0 bytes */
+ NVM_BOOTLOADER_SIZE_0,
+};
+
+/**
+ * \brief EEPROM emulator size.
+ *
+ * Available space in flash dedicated for EEPROM emulator in bytes.
+ *
+ */
+enum nvm_eeprom_emulator_size {
+ /** EEPROM Size for EEPROM emulation is 16384 bytes */
+ NVM_EEPROM_EMULATOR_SIZE_16384,
+ /** EEPROM Size for EEPROM emulation is 8192 bytes */
+ NVM_EEPROM_EMULATOR_SIZE_8192,
+ /** EEPROM Size for EEPROM emulation is 4096 bytes */
+ NVM_EEPROM_EMULATOR_SIZE_4096,
+ /** EEPROM Size for EEPROM emulation is 2048 bytes */
+ NVM_EEPROM_EMULATOR_SIZE_2048,
+ /** EEPROM Size for EEPROM emulation is 1024 bytes */
+ NVM_EEPROM_EMULATOR_SIZE_1024,
+ /** EEPROM Size for EEPROM emulation is 512 bytes */
+ NVM_EEPROM_EMULATOR_SIZE_512,
+ /** EEPROM Size for EEPROM emulation is 256 bytes */
+ NVM_EEPROM_EMULATOR_SIZE_256,
+ /** EEPROM Size for EEPROM emulation is 0 bytes */
+ NVM_EEPROM_EMULATOR_SIZE_0,
+};
+
+/**
+ * \brief BOD33 Action.
+ *
+ * What action should be triggered when BOD33 is detected.
+ *
+ */
+enum nvm_bod33_action {
+ /** No action */
+ NVM_BOD33_ACTION_NONE,
+ /** The BOD33 generates a reset */
+ NVM_BOD33_ACTION_RESET,
+ /** The BOD33 generates an interrupt */
+ NVM_BOD33_ACTION_INTERRUPT,
+};
+
+#ifdef FEATURE_BOD12
+/**
+ * \brief BOD12 Action.
+ *
+ * What action should be triggered when BOD12 is detected.
+ *
+ */
+enum nvm_bod12_action {
+ /** No action */
+ NVM_BOD12_ACTION_NONE,
+ /** The BOD12 generates a reset */
+ NVM_BOD12_ACTION_RESET,
+ /** The BOD12 generates an interrupt */
+ NVM_BOD12_ACTION_INTERRUPT,
+};
+#endif
+
+/**
+ * \brief WDT Window time-out period.
+ *
+ * Window mode time-out period in clock cycles.
+ *
+ */
+enum nvm_wdt_window_timeout {
+ /** 8 clock cycles */
+ NVM_WDT_WINDOW_TIMEOUT_PERIOD_8,
+ /** 16 clock cycles */
+ NVM_WDT_WINDOW_TIMEOUT_PERIOD_16,
+ /** 32 clock cycles */
+ NVM_WDT_WINDOW_TIMEOUT_PERIOD_32,
+ /** 64 clock cycles */
+ NVM_WDT_WINDOW_TIMEOUT_PERIOD_64,
+ /** 128 clock cycles */
+ NVM_WDT_WINDOW_TIMEOUT_PERIOD_128,
+ /** 256 clock cycles */
+ NVM_WDT_WINDOW_TIMEOUT_PERIOD_256,
+ /** 512 clock cycles */
+ NVM_WDT_WINDOW_TIMEOUT_PERIOD_512,
+ /** 1024 clock cycles */
+ NVM_WDT_WINDOW_TIMEOUT_PERIOD_1024,
+ /** 2048 clock cycles */
+ NVM_WDT_WINDOW_TIMEOUT_PERIOD_2048,
+ /** 4096 clock cycles */
+ NVM_WDT_WINDOW_TIMEOUT_PERIOD_4096,
+ /** 8192 clock cycles */
+ NVM_WDT_WINDOW_TIMEOUT_PERIOD_8192,
+ /** 16384 clock cycles */
+ NVM_WDT_WINDOW_TIMEOUT_PERIOD_16384,
+};
+
+/**
+ * \brief WDT Early warning offset.
+ *
+ * This setting determine how many GCLK_WDT cycles before a watchdog time-out period
+ * an early warning interrupt should be triggered.
+ *
+ */
+enum nvm_wdt_early_warning_offset {
+ /** 8 clock cycles */
+ NVM_WDT_EARLY_WARNING_OFFSET_8,
+ /** 16 clock cycles */
+ NVM_WDT_EARLY_WARNING_OFFSET_16,
+ /** 32 clock cycles */
+ NVM_WDT_EARLY_WARNING_OFFSET_32,
+ /** 64 clock cycles */
+ NVM_WDT_EARLY_WARNING_OFFSET_64,
+ /** 128 clock cycles */
+ NVM_WDT_EARLY_WARNING_OFFSET_128,
+ /** 256 clock cycles */
+ NVM_WDT_EARLY_WARNING_OFFSET_256,
+ /** 512 clock cycles */
+ NVM_WDT_EARLY_WARNING_OFFSET_512,
+ /** 1024 clock cycles */
+ NVM_WDT_EARLY_WARNING_OFFSET_1024,
+ /** 2048 clock cycles */
+ NVM_WDT_EARLY_WARNING_OFFSET_2048,
+ /** 4096 clock cycles */
+ NVM_WDT_EARLY_WARNING_OFFSET_4096,
+ /** 8192 clock cycles */
+ NVM_WDT_EARLY_WARNING_OFFSET_8192,
+ /** 16384 clock cycles */
+ NVM_WDT_EARLY_WARNING_OFFSET_16384,
+};
+
+/**
+ * \brief NVM user row fuse setting structure.
+ *
+ * This structure contain the layout of the first 64 bits of the user row
+ * which contain the fuse settings.
+ */
+struct nvm_fusebits {
+ /** Bootloader size */
+ enum nvm_bootloader_size bootloader_size;
+ /** EEPROM emulation area size */
+ enum nvm_eeprom_emulator_size eeprom_size;
+#if (SAMC20) || (SAMC21)
+ /** BODVDD Threshold level at power on */
+ uint8_t bodvdd_level;
+ /** BODVDD Enable at power on */
+ bool bodvdd_enable;
+ /** BODVDD Action at power on */
+ enum nvm_bod33_action bodvdd_action;
+ /* BODVDD Hysteresis at power on */
+ bool bodvdd_hysteresis;
+#else
+ /** BOD33 Threshold level at power on */
+ uint8_t bod33_level;
+ /** BOD33 Enable at power on */
+ bool bod33_enable;
+ /** BOD33 Action at power on */
+ enum nvm_bod33_action bod33_action;
+ /* BOD33 Hysteresis at power on */
+ bool bod33_hysteresis;
+#endif
+ /** WDT Enable at power on */
+ bool wdt_enable;
+ /** WDT Always-on at power on */
+ bool wdt_always_on;
+ /** WDT Period at power on */
+ uint8_t wdt_timeout_period;
+ /** WDT Window mode time-out at power on */
+ enum nvm_wdt_window_timeout wdt_window_timeout;
+ /** WDT Early warning interrupt time offset at power on */
+ enum nvm_wdt_early_warning_offset wdt_early_warning_offset;
+ /** WDT Window mode enabled at power on */
+ bool wdt_window_mode_enable_at_poweron;
+ /** NVM Lock bits */
+ uint16_t lockbits;
+#ifdef FEATURE_BOD12
+ /** BOD12 Threshold level at power on */
+ uint8_t bod12_level;
+ /** BOD12 Enable at power on */
+ bool bod12_enable;
+ /** BOD12 Action at power on */
+ enum nvm_bod12_action bod12_action;
+ /* BOD12 Hysteresis at power on */
+ bool bod12_hysteresis;
+#endif
+};
+
+/**
+ * \name Configuration and Initialization
+ * @{
+ */
+
+/**
+ * \brief Initializes an NVM controller configuration structure to defaults.
+ *
+ * Initializes a given NVM controller configuration structure to a set of
+ * known default values. This function should be called on all new
+ * instances of these configuration structures before being modified by the
+ * user application.
+ *
+ * The default configuration is as follows:
+ * \li Power reduction mode enabled after sleep mode until first NVM access
+ * \li Automatic page write mode disabled
+ * \li Number of FLASH wait states left unchanged
+ *
+ * \param[out] config Configuration structure to initialize to default values
+ *
+ */
+static inline void nvm_get_config_defaults(
+ struct nvm_config *const config)
+{
+ /* Sanity check the parameters */
+ Assert(config);
+
+ /* Write the default configuration for the NVM configuration */
+ config->sleep_power_mode = NVM_SLEEP_POWER_MODE_WAKEONACCESS;
+ config->manual_page_write = true;
+ config->wait_states = NVMCTRL->CTRLB.bit.RWS;
+ config->disable_cache = false;
+#if (SAMC20) || (SAMC21)
+ config->disable_rww_cache = false;
+#endif
+ config->cache_readmode = NVM_CACHE_READMODE_NO_MISS_PENALTY;
+}
+
+enum status_code nvm_set_config(
+ const struct nvm_config *const config);
+
+/**
+ * \brief Checks if the NVM controller is ready to accept a new command.
+ *
+ * Checks the NVM controller to determine if it is currently busy execution an
+ * operation, or ready for a new command.
+ *
+ * \return Busy state of the NVM controller.
+ *
+ * \retval true If the hardware module is ready for a new command
+ * \retval false If the hardware module is busy executing a command
+ *
+ */
+static inline bool nvm_is_ready(void)
+{
+ /* Get a pointer to the module hardware instance */
+ Nvmctrl *const nvm_module = NVMCTRL;
+
+ return nvm_module->INTFLAG.reg & NVMCTRL_INTFLAG_READY;
+}
+
+/** @} */
+
+/**
+ * \name NVM Access Management
+ * @{
+ */
+
+void nvm_get_parameters(
+ struct nvm_parameters *const parameters);
+
+enum status_code nvm_write_buffer(
+ const uint32_t destination_address,
+ const uint8_t *buffer,
+ uint16_t length);
+
+enum status_code nvm_read_buffer(
+ const uint32_t source_address,
+ uint8_t *const buffer,
+ uint16_t length);
+
+enum status_code nvm_update_buffer(
+ const uint32_t destination_address,
+ uint8_t *const buffer,
+ uint16_t offset,
+ uint16_t length);
+
+enum status_code nvm_erase_row(
+ const uint32_t row_address);
+
+enum status_code nvm_execute_command(
+ const enum nvm_command command,
+ const uint32_t address,
+ const uint32_t parameter);
+
+enum status_code nvm_get_fuses(struct nvm_fusebits *fusebits);
+enum status_code nvm_set_fuses(struct nvm_fusebits *fb);
+
+bool nvm_is_page_locked(uint16_t page_number);
+
+/**
+ * \brief Retrieves the error code of the last issued NVM operation.
+ *
+ * Retrieves the error code from the last executed NVM operation. Once
+ * retrieved, any error state flags in the controller are cleared.
+ *
+ * \note The \ref nvm_is_ready() function is an exception. Thus, errors
+ * retrieved after running this function should be valid for the function
+ * executed before \ref nvm_is_ready().
+ *
+ * \return Error caused by the last NVM operation.
+ *
+ * \retval NVM_ERROR_NONE No error occurred in the last NVM operation
+ *
+ * \retval NVM_ERROR_LOCK The last NVM operation attempted to access a locked
+ * region
+ * \retval NVM_ERROR_PROG An invalid NVM command was issued
+ */
+static inline enum nvm_error nvm_get_error(void)
+{
+ enum nvm_error ret_val;
+
+ /* Get a pointer to the module hardware instance */
+ Nvmctrl *const nvm_module = NVMCTRL;
+
+ /* Mask out non-error bits */
+ ret_val = (enum nvm_error)(nvm_module->STATUS.reg & NVM_ERRORS_MASK);
+
+ /* Clear error flags */
+ nvm_module->STATUS.reg = NVM_ERRORS_MASK;
+
+ /* Return error code from the NVM controller */
+ return ret_val;
+}
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @} */
+
+/**
+ * \page asfdoc_sam0_nvm_extra Extra Information for NVM Driver
+ *
+ * \section asfdoc_sam0_nvm_extra_acronyms Acronyms
+ * The table below presents the acronyms used in this module:
+ *
+ * <table>
+ * <tr>
+ * <th>Acronym</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td>NVM</td>
+ * <td>Non-Volatile Memory</td>
+ * </tr>
+ * <tr>
+ * <td>EEPROM</td>
+ * <td>Electrically Erasable Programmable Read-Only Memory</td>
+ * </tr>
+ * </table>
+ *
+ *
+ * \section asfdoc_sam0_nvm_extra_dependencies Dependencies
+ * This driver has the following dependencies:
+ *
+ * - None
+ *
+ *
+ * \section asfdoc_sam0_nvm_extra_errata Errata
+ * There are no errata related to this driver.
+ *
+ *
+ * \section asfdoc_sam0_nvm_extra_history Module History
+ * An overview of the module history is presented in the table below, with
+ * details on the enhancements and fixes made to the module since its first
+ * release. The current version of this corresponds to the newest version in
+ * the table.
+ *
+ * <table>
+ * <tr>
+ * <th>Changelog</th>
+ * </tr>
+ * <tr>
+ * <td>Removed BOD12 reference, removed nvm_set_fuses() API</td>
+ * </tr>
+ * <tr>
+ * <td>Added functions to read/write fuse settings</td>
+ * </tr>
+ * <tr>
+ * <td>Added support for NVM cache configuration</td>
+ * </tr>
+ * <tr>
+ * <td>Updated initialization function to also enable the digital interface
+ * clock to the module if it is disabled</td>
+ * </tr>
+ * <tr>
+ * <td>Initial Release</td>
+ * </tr>
+ * </table>
+ */
+
+/**
+ * \page asfdoc_sam0_nvm_exqsg Examples for NVM Driver
+ *
+ * This is a list of the available Quick Start guides (QSGs) and example
+ * applications for \ref asfdoc_sam0_nvm_group. QSGs are simple examples with
+ * step-by-step instructions to configure and use this driver in a selection of
+ * use cases. Note that a QSG can be compiled as a standalone application or be
+ * added to the user application.
+ *
+ * - \subpage asfdoc_sam0_nvm_basic_use_case
+ *
+ * \page asfdoc_sam0_nvm_document_revision_history Document Revision History
+ *
+ * <table>
+ * <tr>
+ * <th>Doc. Rev.</th>
+ * <th>Date</th>
+ * <th>Comments</th>
+ * </tr>
+ * <tr>
+ * <td>42114E</td>
+ * <td>12/2015</td>
+ * <td>Added support for SAM L21/L22, SAM C21, SAM D09, SAMR30 and SAM DA1</td>
+ * </tr>
+ * <tr>
+ * <td>42114D</td>
+ * <td>12/2014</td>
+ * <td>Added support for SAM R21 and SAM D10/D11</td>
+ * </tr>
+ * <tr>
+ * <td>42114C</td>
+ * <td>01/2014</td>
+ * <td>Added support for SAM D21</td>
+ * </tr>
+ * <tr>
+ * <td>42114B</td>
+ * <td>06/2013</td>
+ * <td>Corrected documentation typos</td>
+ * </tr>
+ * <tr>
+ * <td>42114A</td>
+ * <td>06/2013</td>
+ * <td>Initial document release</td>
+ * </tr>
+ * </table>
+ */
+
+#endif /* NVM_H_INCLUDED */
diff --git a/atmel-samd/asf/version.h b/atmel-samd/asf/version.h
new file mode 100644
index 000000000..de93d07ac
--- /dev/null
+++ b/atmel-samd/asf/version.h
@@ -0,0 +1,3 @@
+// Update this when the library is updated. Its not included by default.
+
+#define ASF_VERSION "3.32.0"
diff --git a/atmel-samd/boards/samd21x18-bootloader.ld b/atmel-samd/boards/samd21x18-bootloader.ld
index 974d78626..89ff084fb 100644
--- a/atmel-samd/boards/samd21x18-bootloader.ld
+++ b/atmel-samd/boards/samd21x18-bootloader.ld
@@ -5,7 +5,7 @@
/* Specify the memory areas */
MEMORY
{
- FLASH (rx) : ORIGIN = 0x00000000+0x2000, LENGTH = 0x00040000-0x2000 /* Leave 8KiB for the bootloader. */
+ FLASH (rx) : ORIGIN = 0x00000000+0x2000, LENGTH = 0x00040000 - 0x2000 - 0x010000 /* Leave 8KiB for the bootloader and 64k for the flash file system. */
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x008000 /* 32 KiB */
}
diff --git a/atmel-samd/boards/samd21x18.ld b/atmel-samd/boards/samd21x18.ld
index 6c1e6c01e..0f1168c57 100644
--- a/atmel-samd/boards/samd21x18.ld
+++ b/atmel-samd/boards/samd21x18.ld
@@ -5,7 +5,7 @@
/* Specify the memory areas */
MEMORY
{
- FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
+ FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000 - 0x010000 /* Leave 64k for the flash file system. */
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x008000 /* 32 KiB */
}
diff --git a/atmel-samd/builtin_open.c b/atmel-samd/builtin_open.c
new file mode 100644
index 000000000..697eec8ea
--- /dev/null
+++ b/atmel-samd/builtin_open.c
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/runtime.h"
+#include "extmod/vfs_fat_file.h"
+
+MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, fatfs_builtin_open);
diff --git a/atmel-samd/fatfs_port.c b/atmel-samd/fatfs_port.c
new file mode 100644
index 000000000..dcb02f613
--- /dev/null
+++ b/atmel-samd/fatfs_port.c
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mphal.h"
+#include "py/runtime.h"
+#include "lib/fatfs/ff.h" /* FatFs lower layer API */
+#include "lib/fatfs/diskio.h" /* FatFs lower layer API */
+
+const PARTITION VolToPart[MICROPY_FATFS_VOLUMES] = {
+ {0, 1}, // Logical drive 0 ==> Physical drive 0, 1st partition
+ {1, 0}, // Logical drive 1 ==> Physical drive 1 (auto detection)
+ {2, 0}, // Logical drive 2 ==> Physical drive 2 (auto detection)
+ {3, 0}, // Logical drive 3 ==> Physical drive 3 (auto detection)
+ /*
+ {0, 2}, // Logical drive 2 ==> Physical drive 0, 2nd partition
+ {0, 3}, // Logical drive 3 ==> Physical drive 0, 3rd partition
+ */
+};
+
+DWORD get_fattime(void) {
+ // TODO(tannewt): Support the RTC.
+ return ((2016) << 25) | ((9) << 21) | ((1) << 16) | ((16) << 11) | ((43) << 5) | (35 / 2);
+}
diff --git a/atmel-samd/main.c b/atmel-samd/main.c
index 15b281cbc..c4db13470 100644
--- a/atmel-samd/main.c
+++ b/atmel-samd/main.c
@@ -8,7 +8,10 @@
#include "py/runtime.h"
#include "py/repl.h"
#include "py/gc.h"
+
+#include "lib/fatfs/ff.h"
#include "lib/utils/pyexec.h"
+#include "extmod/fsusermount.h"
#include "asf/common/services/sleepmgr/sleepmgr.h"
#include "asf/common/services/usb/udc/udc.h"
@@ -19,8 +22,11 @@
#include "mpconfigboard.h"
#include "modmachine_pin.h"
+#include "storage.h"
#include "uart.h"
+fs_user_mount_t fs_user_mount_flash;
+
void do_str(const char *src, mp_parse_input_kind_t input_kind) {
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
if (lex == NULL) {
@@ -41,6 +47,114 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) {
}
}
+static const char fresh_boot_py[] =
+"# boot.py -- run on boot-up\r\n"
+"# can run arbitrary Python, but best to keep it minimal\r\n"
+"\r\n"
+;
+
+static const char fresh_main_py[] =
+"# main.py -- put your code here!\r\n"
+;
+
+static const char fresh_readme_txt[] =
+"This is a MicroPython board\r\n"
+"\r\n"
+"You can get started right away by writing your Python code in 'main.py'.\r\n"
+"\r\n"
+"For a serial prompt:\r\n"
+" - Windows: you need to go to 'Device manager', right click on the unknown device,\r\n"
+" then update the driver software, using the 'pybcdc.inf' file found on this drive.\r\n"
+" Then use a terminal program like Hyperterminal or putty.\r\n"
+" - Mac OS X: use the command: screen /dev/tty.usbmodem*\r\n"
+" - Linux: use the command: screen /dev/ttyACM0\r\n"
+"\r\n"
+"Please visit http://micropython.org/help/ for further help.\r\n"
+;
+
+// we don't make this function static because it needs a lot of stack and we
+// want it to be executed without using stack within main() function
+void init_flash_fs() {
+ // init the vfs object
+ fs_user_mount_t *vfs = &fs_user_mount_flash;
+ vfs->str = "/flash";
+ vfs->len = 6;
+ vfs->flags = 0;
+ flash_init_vfs(vfs);
+
+ // put the flash device in slot 0 (it will be unused at this point)
+ MP_STATE_PORT(fs_user_mount)[0] = vfs;
+
+ // try to mount the flash
+ FRESULT res = f_mount(&vfs->fatfs, vfs->str, 1);
+
+ if (res == FR_NO_FILESYSTEM) {
+ // no filesystem, or asked to reset it, so create a fresh one
+
+ res = f_mkfs("/flash", 0, 0);
+ if (res == FR_OK) {
+ // success creating fresh LFS
+ } else {
+ printf("PYB: can't create flash filesystem\n");
+ MP_STATE_PORT(fs_user_mount)[0] = NULL;
+ return;
+ }
+
+ // set label
+ f_setlabel("/flash/internalflash");
+
+ // create empty main.py
+ FIL fp;
+ f_open(&fp, "/flash/main.py", FA_WRITE | FA_CREATE_ALWAYS);
+ UINT n;
+ f_write(&fp, fresh_main_py, sizeof(fresh_main_py) - 1 /* don't count null terminator */, &n);
+ f_close(&fp);
+
+ // TODO(tannewt): Create an .inf driver file for Windows.
+
+ // create readme file
+ f_open(&fp, "/flash/README.txt", FA_WRITE | FA_CREATE_ALWAYS);
+ f_write(&fp, fresh_readme_txt, sizeof(fresh_readme_txt) - 1 /* don't count null terminator */, &n);
+ f_close(&fp);
+ } else if (res == FR_OK) {
+ // mount successful
+ } else {
+ printf("PYB: can't mount flash\n");
+ MP_STATE_PORT(fs_user_mount)[0] = NULL;
+ return;
+ }
+
+ // The current directory is used as the boot up directory.
+ // It is set to the internal flash filesystem by default.
+ f_chdrive("/flash");
+
+ // Make sure we have a /flash/boot.py. Create it if needed.
+ FILINFO fno;
+#if _USE_LFN
+ fno.lfname = NULL;
+ fno.lfsize = 0;
+#endif
+ res = f_stat("/flash/boot.py", &fno);
+ if (res == FR_OK) {
+ if (fno.fattrib & AM_DIR) {
+ // exists as a directory
+ // TODO handle this case
+ // see http://elm-chan.org/fsw/ff/img/app2.c for a "rm -rf" implementation
+ } else {
+ // exists as a file, good!
+ }
+ } else {
+ // doesn't exist, create fresh file
+
+ FIL fp;
+ f_open(&fp, "/flash/boot.py", FA_WRITE | FA_CREATE_ALWAYS);
+ UINT n;
+ f_write(&fp, fresh_boot_py, sizeof(fresh_boot_py) - 1 /* don't count null terminator */, &n);
+ // TODO check we could write n bytes
+ f_close(&fp);
+ }
+}
+
static char *stack_top;
static char heap[8192];
@@ -63,6 +177,10 @@ int main(int argc, char **argv) {
pin_init0();
+ // Initialise the local flash filesystem.
+ // Create it if needed, mount in on /flash, and set it as current dir.
+ init_flash_fs();
+
#if MICROPY_REPL_EVENT_DRIVEN
pyexec_event_repl_init();
for (;;) {
@@ -98,11 +216,6 @@ mp_import_stat_t mp_import_stat(const char *path) {
return MP_IMPORT_STAT_NO_EXIST;
}
-mp_obj_t mp_builtin_open(uint n_args, const mp_obj_t *args, mp_map_t *kwargs) {
- return mp_const_none;
-}
-MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open);
-
void mp_keyboard_interrupt(void) {
MP_STATE_VM(mp_pending_exception) = MP_STATE_PORT(mp_kbd_exception);
}
diff --git a/atmel-samd/modmachine.c b/atmel-samd/modmachine.c
index d59cf84cf..e5ba5ae98 100644
--- a/atmel-samd/modmachine.c
+++ b/atmel-samd/modmachine.c
@@ -35,6 +35,7 @@
#include "modmachine_dac.h"
#include "modmachine_pin.h"
#include "modmachine_pwm.h"
+#include "storage.h"
#if MICROPY_PY_MACHINE
@@ -44,6 +45,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&dac_type) },
{ MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pin_type) },
{ MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&pwm_type) },
+ { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&flash_type) },
};
STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table);
diff --git a/atmel-samd/moduos.c b/atmel-samd/moduos.c
new file mode 100644
index 000000000..cd98abfbd
--- /dev/null
+++ b/atmel-samd/moduos.c
@@ -0,0 +1,385 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "py/mpstate.h"
+#include "py/objtuple.h"
+#include "py/objstr.h"
+#include "genhdr/mpversion.h"
+#include "lib/fatfs/ff.h"
+#include "lib/fatfs/diskio.h"
+#include "timeutils.h"
+#include "extmod/vfs_fat_file.h"
+#include "extmod/fsusermount.h"
+
+/// \module os - basic "operating system" services
+///
+/// The `os` module contains functions for filesystem access and `urandom`.
+///
+/// The filesystem has `/` as the root directory, and the available physical
+/// drives are accessible from here. They are currently:
+///
+/// /flash -- the internal flash filesystem
+/// /sd -- the SD card (if it exists)
+///
+/// On boot up, the current directory is `/flash` if no SD card is inserted,
+/// otherwise it is `/sd`.
+
+STATIC const qstr os_uname_info_fields[] = {
+ MP_QSTR_sysname, MP_QSTR_nodename,
+ MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine
+};
+STATIC const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, "samd21");
+STATIC const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, "samd21");
+STATIC const MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING);
+STATIC const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE);
+STATIC const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME);
+STATIC MP_DEFINE_ATTRTUPLE(
+ os_uname_info_obj,
+ os_uname_info_fields,
+ 5,
+ (mp_obj_t)&os_uname_info_sysname_obj,
+ (mp_obj_t)&os_uname_info_nodename_obj,
+ (mp_obj_t)&os_uname_info_release_obj,
+ (mp_obj_t)&os_uname_info_version_obj,
+ (mp_obj_t)&os_uname_info_machine_obj
+);
+
+STATIC mp_obj_t os_uname(void) {
+ return (mp_obj_t)&os_uname_info_obj;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname);
+
+/// \function chdir(path)
+/// Change current directory.
+STATIC mp_obj_t os_chdir(mp_obj_t path_in) {
+ const char *path;
+ path = mp_obj_str_get_str(path_in);
+
+ FRESULT res = f_chdrive(path);
+
+ if (res == FR_OK) {
+ res = f_chdir(path);
+ }
+
+ if (res != FR_OK) {
+ // TODO should be mp_type_FileNotFoundError
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "No such file or directory: '%s'", path));
+ }
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_chdir_obj, os_chdir);
+
+/// \function getcwd()
+/// Get the current directory.
+STATIC mp_obj_t os_getcwd(void) {
+ char buf[MICROPY_ALLOC_PATH_MAX + 1];
+ FRESULT res = f_getcwd(buf, sizeof buf);
+
+ if (res != FR_OK) {
+ nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(fresult_to_errno_table[res])));
+ }
+
+ return mp_obj_new_str(buf, strlen(buf), false);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_getcwd_obj, os_getcwd);
+
+/// \function listdir([dir])
+/// With no argument, list the current directory. Otherwise list the given directory.
+STATIC mp_obj_t os_listdir(mp_uint_t n_args, const mp_obj_t *args) {
+ bool is_str_type = true;
+ const char *path;
+ if (n_args == 1) {
+ if (mp_obj_get_type(args[0]) == &mp_type_bytes) {
+ is_str_type = false;
+ }
+ path = mp_obj_str_get_str(args[0]);
+ } else {
+ path = "";
+ }
+
+ // "hack" to list root directory
+ if (path[0] == '/' && path[1] == '\0') {
+ mp_obj_t dir_list = mp_obj_new_list(0, NULL);
+ for (size_t i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount)); ++i) {
+ fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount)[i];
+ if (vfs != NULL) {
+ mp_obj_list_append(dir_list, mp_obj_new_str(vfs->str + 1, vfs->len - 1, false));
+ }
+ }
+ return dir_list;
+ }
+
+ return fat_vfs_listdir(path, is_str_type);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(os_listdir_obj, 0, 1, os_listdir);
+
+/// \function mkdir(path)
+/// Create a new directory.
+STATIC mp_obj_t os_mkdir(mp_obj_t path_o) {
+ const char *path = mp_obj_str_get_str(path_o);
+ FRESULT res = f_mkdir(path);
+ switch (res) {
+ case FR_OK:
+ return mp_const_none;
+ case FR_EXIST:
+ // TODO should be FileExistsError
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "File exists: '%s'", path));
+ default:
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error creating directory '%s'", path));
+ }
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_mkdir_obj, os_mkdir);
+
+/// \function remove(path)
+/// Remove a file.
+STATIC mp_obj_t os_remove(mp_obj_t path_o) {
+ const char *path = mp_obj_str_get_str(path_o);
+ // TODO check that path is actually a file before trying to unlink it
+ FRESULT res = f_unlink(path);
+ switch (res) {
+ case FR_OK:
+ return mp_const_none;
+ default:
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error removing file '%s'", path));
+ }
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_remove_obj, os_remove);
+
+/// \function rename(old_path, new_path)
+/// Rename a file
+STATIC mp_obj_t os_rename(mp_obj_t path_in, mp_obj_t path_out) {
+ const char *old_path = mp_obj_str_get_str(path_in);
+ const char *new_path = mp_obj_str_get_str(path_out);
+ FRESULT res = f_rename(old_path, new_path);
+ switch (res) {
+ case FR_OK:
+ return mp_const_none;
+ default:
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error renaming file '%s' to '%s'", old_path, new_path));
+ }
+
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(os_rename_obj, os_rename);
+
+/// \function rmdir(path)
+/// Remove a directory.
+STATIC mp_obj_t os_rmdir(mp_obj_t path_o) {
+ const char *path = mp_obj_str_get_str(path_o);
+ // TODO check that path is actually a directory before trying to unlink it
+ FRESULT res = f_unlink(path);
+ switch (res) {
+ case FR_OK:
+ return mp_const_none;
+ default:
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error removing directory '%s'", path));
+ }
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_rmdir_obj, os_rmdir);
+
+// Checks for path equality, ignoring trailing slashes:
+// path_equal(/, /) -> true
+// path_equal(/flash//, /flash) -> true
+// second argument must be in canonical form (meaning no trailing slash, unless it's just /)
+STATIC bool path_equal(const char *path, const char *path_canonical) {
+ for (; *path_canonical != '\0' && *path == *path_canonical; ++path, ++path_canonical) {
+ }
+ if (*path_canonical != '\0') {
+ return false;
+ }
+ for (; *path == '/'; ++path) {
+ }
+ return *path == '\0';
+}
+
+/// \function stat(path)
+/// Get the status of a file or directory.
+STATIC mp_obj_t os_stat(mp_obj_t path_in) {
+ const char *path = mp_obj_str_get_str(path_in);
+
+ FILINFO fno;
+#if _USE_LFN
+ fno.lfname = NULL;
+ fno.lfsize = 0;
+#endif
+
+ FRESULT res;
+ if (path_equal(path, "/")) {
+ // stat root directory
+ fno.fsize = 0;
+ fno.fdate = 0;
+ fno.ftime = 0;
+ fno.fattrib = AM_DIR;
+ } else {
+ res = FR_NO_PATH;
+ for (size_t i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount)); ++i) {
+ fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount)[i];
+ if (vfs != NULL && path_equal(path, vfs->str)) {
+ // stat mounted device directory
+ fno.fsize = 0;
+ fno.fdate = 0;
+ fno.ftime = 0;
+ fno.fattrib = AM_DIR;
+ res = FR_OK;
+ }
+ }
+ if (res == FR_NO_PATH) {
+ // stat normal file
+ res = f_stat(path, &fno);
+ }
+ if (res != FR_OK) {
+ nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError,
+ MP_OBJ_NEW_SMALL_INT(fresult_to_errno_table[res])));
+ }
+ }
+
+ mp_obj_tuple_t *t = mp_obj_new_tuple(10, NULL);
+ mp_int_t mode = 0;
+ if (fno.fattrib & AM_DIR) {
+ mode |= 0x4000; // stat.S_IFDIR
+ } else {
+ mode |= 0x8000; // stat.S_IFREG
+ }
+ mp_int_t seconds = timeutils_seconds_since_2000(
+ 1980 + ((fno.fdate >> 9) & 0x7f),
+ (fno.fdate >> 5) & 0x0f,
+ fno.fdate & 0x1f,
+ (fno.ftime >> 11) & 0x1f,
+ (fno.ftime >> 5) & 0x3f,
+ 2 * (fno.ftime & 0x1f)
+ );
+ t->items[0] = MP_OBJ_NEW_SMALL_INT(mode); // st_mode
+ t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino
+ t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev
+ t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // st_nlink
+ t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid
+ t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid
+ t->items[6] = MP_OBJ_NEW_SMALL_INT(fno.fsize); // st_size
+ t->items[7] = MP_OBJ_NEW_SMALL_INT(seconds); // st_atime
+ t->items[8] = MP_OBJ_NEW_SMALL_INT(seconds); // st_mtime
+ t->items[9] = MP_OBJ_NEW_SMALL_INT(seconds); // st_ctime
+
+ return t;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_stat_obj, os_stat);
+
+STATIC mp_obj_t os_statvfs(mp_obj_t path_in) {
+ const char *path = mp_obj_str_get_str(path_in);
+
+ DWORD nclst;
+ FATFS *fatfs;
+ FRESULT res = f_getfree(path, &nclst, &fatfs);
+ if (res != FR_OK) {
+ goto error;
+ }
+
+ mp_obj_tuple_t *t = mp_obj_new_tuple(10, NULL);
+
+ t->items[0] = MP_OBJ_NEW_SMALL_INT(fatfs->csize * 512); // f_bsize - block size
+ t->items[1] = t->items[0]; // f_frsize - fragment size
+ t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // f_blocks - total number of blocks
+ t->items[3] = MP_OBJ_NEW_SMALL_INT(nclst); // f_bfree - number of free blocks
+ t->items[4] = t->items[3]; // f_bavail - free blocks avail to unpriviledged users
+ t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // f_files - # inodes
+ t->items[6] = MP_OBJ_NEW_SMALL_INT(0); // f_ffree - # free inodes
+ t->items[7] = MP_OBJ_NEW_SMALL_INT(0); // f_favail - # free inodes avail to unpriviledges users
+ t->items[8] = MP_OBJ_NEW_SMALL_INT(0); // f_flags
+ t->items[9] = MP_OBJ_NEW_SMALL_INT(_MAX_LFN); // f_namemax
+
+ return t;
+
+error:
+ nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(fresult_to_errno_table[res])));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_statvfs_obj, os_statvfs);
+
+/// \function sync()
+/// Sync all filesystems.
+STATIC mp_obj_t os_sync(void) {
+ disk_ioctl(0, CTRL_SYNC, NULL);
+ disk_ioctl(1, CTRL_SYNC, NULL);
+ disk_ioctl(2, CTRL_SYNC, NULL);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_0(mod_os_sync_obj, os_sync);
+
+#if MICROPY_HW_ENABLE_RNG
+/// \function urandom(n)
+/// Return a bytes object with n random bytes, generated by the hardware
+/// random number generator.
+STATIC mp_obj_t os_urandom(mp_obj_t num) {
+ mp_int_t n = mp_obj_get_int(num);
+ vstr_t vstr;
+ vstr_init_len(&vstr, n);
+ for (int i = 0; i < n; i++) {
+ vstr.buf[i] = rng_get();
+ }
+ return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom);
+#endif
+
+STATIC const mp_map_elem_t os_module_globals_table[] = {
+ { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_uos) },
+
+ { MP_OBJ_NEW_QSTR(MP_QSTR_uname), (mp_obj_t)&os_uname_obj },
+
+ { MP_OBJ_NEW_QSTR(MP_QSTR_chdir), (mp_obj_t)&os_chdir_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_getcwd), (mp_obj_t)&os_getcwd_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_listdir), (mp_obj_t)&os_listdir_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_mkdir), (mp_obj_t)&os_mkdir_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_remove), (mp_obj_t)&os_remove_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_rename),(mp_obj_t)&os_rename_obj},
+ { MP_OBJ_NEW_QSTR(MP_QSTR_rmdir), (mp_obj_t)&os_rmdir_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_stat), (mp_obj_t)&os_stat_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_statvfs), (mp_obj_t)&os_statvfs_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_unlink), (mp_obj_t)&os_remove_obj }, // unlink aliases to remove
+
+ { MP_OBJ_NEW_QSTR(MP_QSTR_sync), (mp_obj_t)&mod_os_sync_obj },
+
+ /// \constant sep - separation character used in paths
+ { MP_OBJ_NEW_QSTR(MP_QSTR_sep), MP_OBJ_NEW_QSTR(MP_QSTR__slash_) },
+
+#if MICROPY_HW_ENABLE_RNG
+ { MP_OBJ_NEW_QSTR(MP_QSTR_urandom), (mp_obj_t)&os_urandom_obj },
+#endif
+
+ // these are MicroPython extensions
+ { MP_OBJ_NEW_QSTR(MP_QSTR_mount), (mp_obj_t)&fsuser_mount_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_umount), (mp_obj_t)&fsuser_umount_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_mkfs), (mp_obj_t)&fsuser_mkfs_obj },
+};
+
+STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table);
+
+const mp_obj_module_t uos_module = {
+ .base = { &mp_type_module },
+ .name = MP_QSTR_uos,
+ .globals = (mp_obj_dict_t*)&os_module_globals,
+};
diff --git a/atmel-samd/mpconfigport.h b/atmel-samd/mpconfigport.h
index c4a0199be..549731b31 100644
--- a/atmel-samd/mpconfigport.h
+++ b/atmel-samd/mpconfigport.h
@@ -3,6 +3,8 @@
#ifndef __INCLUDED_MPCONFIGPORT_H
#define __INCLUDED_MPCONFIGPORT_H
+#define MICROPY_PY_SYS_PLATFORM "Atmel SAMD21"
+
// options to control how Micro Python is built
#define MICROPY_QSTR_BYTES_IN_HASH (1)
@@ -28,8 +30,8 @@
#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE)
#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0)
#define MICROPY_PY_ASYNC_AWAIT (0)
-#define MICROPY_PY_BUILTINS_BYTEARRAY (0)
-#define MICROPY_PY_BUILTINS_MEMORYVIEW (0)
+#define MICROPY_PY_BUILTINS_BYTEARRAY (1)
+#define MICROPY_PY_BUILTINS_MEMORYVIEW (1)
#define MICROPY_PY_BUILTINS_ENUMERATE (0)
#define MICROPY_PY_BUILTINS_FILTER (0)
#define MICROPY_PY_BUILTINS_FROZENSET (0)
@@ -40,8 +42,8 @@
#define MICROPY_PY_BUILTINS_MIN_MAX (0)
#define MICROPY_PY___FILE__ (0)
#define MICROPY_PY_GC (0)
-#define MICROPY_PY_ARRAY (0)
-#define MICROPY_PY_ATTRTUPLE (0)
+#define MICROPY_PY_ARRAY (1)
+#define MICROPY_PY_ATTRTUPLE (1)
#define MICROPY_PY_COLLECTIONS (0)
#define MICROPY_PY_MATH (0)
#define MICROPY_PY_CMATH (0)
@@ -53,6 +55,16 @@
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_NONE)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
+// fatfs configuration used in ffconf.h
+#define MICROPY_FATFS_ENABLE_LFN (1)
+#define MICROPY_FATFS_LFN_CODE_PAGE (437) /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */
+#define MICROPY_FATFS_USE_LABEL (1)
+#define MICROPY_FATFS_RPATH (2)
+#define MICROPY_FATFS_VOLUMES (4)
+#define MICROPY_FATFS_MULTI_PARTITION (1)
+#define MICROPY_FSUSERMOUNT (1)
+
+#define MICROPY_VFS_FAT (1)
#define MICROPY_PY_MACHINE (1)
#define MICROPY_MODULE_WEAK_LINKS (1)
#define MICROPY_REPL_AUTO_INDENT (1)
@@ -78,16 +90,23 @@ typedef long mp_off_t;
#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len)
+// extra built in names to add to the global namespace
+#define MICROPY_PORT_BUILTINS \
+ { MP_OBJ_NEW_QSTR(MP_QSTR_open), (mp_obj_t)&mp_builtin_open_obj },
+
// extra built in modules to add to the list of known ones
extern const struct _mp_obj_module_t machine_module;
+extern const struct _mp_obj_module_t uos_module;
extern const struct _mp_obj_module_t utime_module;
#define MICROPY_PORT_BUILTIN_MODULES \
{ MP_OBJ_NEW_QSTR(MP_QSTR_umachine), (mp_obj_t)&machine_module }, \
+ { MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&uos_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&utime_module } \
#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \
{ MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&machine_module }, \
+ { MP_OBJ_NEW_QSTR(MP_QSTR_os), (mp_obj_t)&uos_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&utime_module } \
// board specific definitions
diff --git a/atmel-samd/storage.c b/atmel-samd/storage.c
new file mode 100644
index 000000000..a4037da4f
--- /dev/null
+++ b/atmel-samd/storage.c
@@ -0,0 +1,290 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "lib/fatfs/ff.h"
+#include "extmod/fsusermount.h"
+
+#include "asf/sam0/drivers/nvm/nvm.h"
+
+#include "storage.h"
+
+#define TOTAL_FLASH_SIZE 0x010000
+
+#define FLASH_MEM_SEG1_START_ADDR (0x00040000 - TOTAL_FLASH_SIZE)
+#define FLASH_PART1_START_BLOCK (0x100)
+#define FLASH_PART1_NUM_BLOCKS (TOTAL_FLASH_SIZE / FLASH_BLOCK_SIZE)
+
+static bool flash_is_initialised = false;
+
+void storage_init(void) {
+ if (!flash_is_initialised) {
+ struct nvm_config config_nvm;
+ nvm_get_config_defaults(&config_nvm);
+ config_nvm.manual_page_write = false;
+ nvm_set_config(&config_nvm);
+ flash_is_initialised = true;
+ }
+}
+
+uint32_t storage_get_block_size(void) {
+ return FLASH_BLOCK_SIZE;
+}
+
+uint32_t storage_get_block_count(void) {
+ return FLASH_PART1_START_BLOCK + FLASH_PART1_NUM_BLOCKS;
+}
+
+void storage_flush(void) {
+}
+
+static void build_partition(uint8_t *buf, int boot, int type, uint32_t start_block, uint32_t num_blocks) {
+ buf[0] = boot;
+
+ if (num_blocks == 0) {
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = 0;
+ } else {
+ buf[1] = 0xff;
+ buf[2] = 0xff;
+ buf[3] = 0xff;
+ }
+
+ buf[4] = type;
+
+ if (num_blocks == 0) {
+ buf[5] = 0;
+ buf[6] = 0;
+ buf[7] = 0;
+ } else {
+ buf[5] = 0xff;
+ buf[6] = 0xff;
+ buf[7] = 0xff;
+ }
+
+ buf[8] = start_block;
+ buf[9] = start_block >> 8;
+ buf[10] = start_block >> 16;
+ buf[11] = start_block >> 24;
+
+ buf[12] = num_blocks;
+ buf[13] = num_blocks >> 8;
+ buf[14] = num_blocks >> 16;
+ buf[15] = num_blocks >> 24;
+}
+
+static uint32_t convert_block_to_flash_addr(uint32_t block) {
+ if (FLASH_PART1_START_BLOCK <= block && block < FLASH_PART1_START_BLOCK + FLASH_PART1_NUM_BLOCKS) {
+ // a block in partition 1
+ block -= FLASH_PART1_START_BLOCK;
+ return FLASH_MEM_SEG1_START_ADDR + block * FLASH_BLOCK_SIZE;
+ }
+ // bad block
+ return -1;
+}
+
+bool storage_read_block(uint8_t *dest, uint32_t block) {
+ //printf("RD %u\n", block);
+ if (block == 0) {
+ // fake the MBR so we can decide on our own partition table
+
+ for (int i = 0; i < 446; i++) {
+ dest[i] = 0;
+ }
+
+ build_partition(dest + 446, 0, 0x01 /* FAT12 */, FLASH_PART1_START_BLOCK, FLASH_PART1_NUM_BLOCKS);
+ build_partition(dest + 462, 0, 0, 0, 0);
+ build_partition(dest + 478, 0, 0, 0, 0);
+ build_partition(dest + 494, 0, 0, 0, 0);
+
+ dest[510] = 0x55;
+ dest[511] = 0xaa;
+
+ return true;
+
+ } else {
+ // non-MBR block, get data from flash memory
+ uint32_t src = convert_block_to_flash_addr(block);
+ if (src == -1) {
+ // bad block number
+ return false;
+ }
+ enum status_code error_code;
+ // A block is made up of multiple pages. Read each page
+ // sequentially.
+ for (int i = 0; i < FLASH_BLOCK_SIZE / NVMCTRL_PAGE_SIZE; i++) {
+ do
+ {
+ error_code = nvm_read_buffer(src + i * NVMCTRL_PAGE_SIZE,
+ dest + i * NVMCTRL_PAGE_SIZE,
+ NVMCTRL_PAGE_SIZE);
+ } while (error_code == STATUS_BUSY);
+ }
+ return true;
+ }
+}
+
+bool storage_write_block(const uint8_t *src, uint32_t block) {
+ if (block == 0) {
+ // can't write MBR, but pretend we did
+ return true;
+
+ } else {
+ // non-MBR block, copy to cache
+ volatile uint32_t dest = convert_block_to_flash_addr(block);
+ if (dest == -1) {
+ // bad block number
+ return false;
+ }
+ enum status_code error_code;
+ // A block is formed by two rows of flash. We must erase each row
+ // before we write back to it.
+ do
+ {
+ error_code = nvm_erase_row(dest);
+ } while (error_code == STATUS_BUSY);
+ if (error_code != STATUS_OK) {
+ return false;
+ }
+ do
+ {
+ error_code = nvm_erase_row(dest + NVMCTRL_ROW_SIZE);
+ } while (error_code == STATUS_BUSY);
+ if (error_code != STATUS_OK) {
+ return false;
+ }
+
+ // A block is made up of multiple pages. Write each page
+ // sequentially.
+ for (int i = 0; i < FLASH_BLOCK_SIZE / NVMCTRL_PAGE_SIZE; i++) {
+ do
+ {
+ error_code = nvm_write_buffer(dest + i * NVMCTRL_PAGE_SIZE,
+ src + i * NVMCTRL_PAGE_SIZE,
+ NVMCTRL_PAGE_SIZE);
+ } while (error_code == STATUS_BUSY);
+ if (error_code != STATUS_OK) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+mp_uint_t storage_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
+ for (size_t i = 0; i < num_blocks; i++) {
+ if (!storage_read_block(dest + i * FLASH_BLOCK_SIZE, block_num + i)) {
+ return 1; // error
+ }
+ }
+ return 0; // success
+}
+
+mp_uint_t storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) {
+ for (size_t i = 0; i < num_blocks; i++) {
+ if (!storage_write_block(src + i * FLASH_BLOCK_SIZE, block_num + i)) {
+ return 1; // error
+ }
+ }
+ return 0; // success
+}
+
+/******************************************************************************/
+// MicroPython bindings
+//
+// Expose the flash as an object with the block protocol.
+
+// there is a singleton Flash object
+STATIC const mp_obj_base_t flash_obj = {&flash_type};
+
+STATIC mp_obj_t flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+ // check arguments
+ mp_arg_check_num(n_args, n_kw, 0, 0, false);
+
+ // return singleton object
+ return (mp_obj_t)&flash_obj;
+}
+
+STATIC mp_obj_t flash_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) {
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE);
+ mp_uint_t ret = storage_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FLASH_BLOCK_SIZE);
+ return MP_OBJ_NEW_SMALL_INT(ret);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(flash_readblocks_obj, flash_readblocks);
+
+STATIC mp_obj_t flash_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) {
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
+ mp_uint_t ret = storage_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FLASH_BLOCK_SIZE);
+ return MP_OBJ_NEW_SMALL_INT(ret);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(flash_writeblocks_obj, flash_writeblocks);
+
+STATIC mp_obj_t flash_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) {
+ mp_int_t cmd = mp_obj_get_int(cmd_in);
+ switch (cmd) {
+ case BP_IOCTL_INIT: storage_init(); return MP_OBJ_NEW_SMALL_INT(0);
+ case BP_IOCTL_DEINIT: storage_flush(); return MP_OBJ_NEW_SMALL_INT(0); // TODO properly
+ case BP_IOCTL_SYNC: storage_flush(); return MP_OBJ_NEW_SMALL_INT(0);
+ case BP_IOCTL_SEC_COUNT: return MP_OBJ_NEW_SMALL_INT(storage_get_block_count());
+ case BP_IOCTL_SEC_SIZE: return MP_OBJ_NEW_SMALL_INT(storage_get_block_size());
+ default: return mp_const_none;
+ }
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(flash_ioctl_obj, flash_ioctl);
+
+STATIC const mp_map_elem_t flash_locals_dict_table[] = {
+ { MP_OBJ_NEW_QSTR(MP_QSTR_readblocks), (mp_obj_t)&flash_readblocks_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_writeblocks), (mp_obj_t)&flash_writeblocks_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_ioctl), (mp_obj_t)&flash_ioctl_obj },
+};
+
+STATIC MP_DEFINE_CONST_DICT(flash_locals_dict, flash_locals_dict_table);
+
+const mp_obj_type_t flash_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_Flash,
+ .make_new = flash_make_new,
+ .locals_dict = (mp_obj_t)&flash_locals_dict,
+};
+
+void flash_init_vfs(fs_user_mount_t *vfs) {
+ vfs->flags |= FSUSER_NATIVE | FSUSER_HAVE_IOCTL;
+ vfs->readblocks[0] = (mp_obj_t)&flash_readblocks_obj;
+ vfs->readblocks[1] = (mp_obj_t)&flash_obj;
+ vfs->readblocks[2] = (mp_obj_t)storage_read_blocks; // native version
+ vfs->writeblocks[0] = (mp_obj_t)&flash_writeblocks_obj;
+ vfs->writeblocks[1] = (mp_obj_t)&flash_obj;
+ vfs->writeblocks[2] = (mp_obj_t)storage_write_blocks; // native version
+ vfs->u.ioctl[0] = (mp_obj_t)&flash_ioctl_obj;
+ vfs->u.ioctl[1] = (mp_obj_t)&flash_obj;
+}
diff --git a/atmel-samd/storage.h b/atmel-samd/storage.h
new file mode 100644
index 000000000..9c1105244
--- /dev/null
+++ b/atmel-samd/storage.h
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#define FLASH_BLOCK_SIZE (512)
+
+#define STORAGE_SYSTICK_MASK (0x1ff) // 512ms
+#define STORAGE_IDLE_TICK(tick) (((tick) & STORAGE_SYSTICK_MASK) == 2)
+
+void storage_init(void);
+uint32_t storage_get_block_size(void);
+uint32_t storage_get_block_count(void);
+void storage_irq_handler(void);
+void storage_flush(void);
+bool storage_read_block(uint8_t *dest, uint32_t block);
+bool storage_write_block(const uint8_t *src, uint32_t block);
+
+// these return 0 on success, non-zero on error
+mp_uint_t storage_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks);
+mp_uint_t storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks);
+
+extern const struct _mp_obj_type_t flash_type;
+
+struct _fs_user_mount_t;
+void flash_init_vfs(struct _fs_user_mount_t *vfs);