diff options
| author | Joey Castillo <joeycastillo@utexas.edu> | 2022-05-06 17:06:27 -0400 | 
|---|---|---|
| committer | Joey Castillo <joeycastillo@utexas.edu> | 2022-05-06 17:12:14 -0400 | 
| commit | d4ebe64af05e2fa163bcea0dd699ae9c8934cc6f (patch) | |
| tree | 0f9db98200a7fc0028ef2549ddfba15918b7465d | |
| parent | 21026c8eb750b35c07bb3ae9fb043fd01e502a06 (diff) | |
| download | Sensor-Watch-d4ebe64af05e2fa163bcea0dd699ae9c8934cc6f.tar.gz Sensor-Watch-d4ebe64af05e2fa163bcea0dd699ae9c8934cc6f.tar.bz2 Sensor-Watch-d4ebe64af05e2fa163bcea0dd699ae9c8934cc6f.zip | |
add support for a small filesystem on the watch
| -rwxr-xr-x | apps/eeprom-emulation-upgrade/Makefile | 10 | ||||
| -rw-r--r-- | apps/eeprom-emulation-upgrade/app.c | 62 | ||||
| -rwxr-xr-x | apps/flash-test/Makefile | 13 | ||||
| -rw-r--r-- | apps/flash-test/app.c | 131 | ||||
| -rw-r--r-- | make.mk | 2 | ||||
| -rw-r--r-- | watch-library/hardware/watch/watch_storage.c | 94 | ||||
| -rw-r--r-- | watch-library/shared/watch/watch.h | 1 | ||||
| -rw-r--r-- | watch-library/shared/watch/watch_storage.h | 92 | ||||
| -rw-r--r-- | watch-library/simulator/watch/watch_storage.c | 32 | 
9 files changed, 437 insertions, 0 deletions
| diff --git a/apps/eeprom-emulation-upgrade/Makefile b/apps/eeprom-emulation-upgrade/Makefile new file mode 100755 index 00000000..5534c178 --- /dev/null +++ b/apps/eeprom-emulation-upgrade/Makefile @@ -0,0 +1,10 @@ +TOP = ../.. +include $(TOP)/make.mk + +INCLUDES += \ +  -I./ + +SRCS += \ +  ./app.c + +include $(TOP)/rules.mk diff --git a/apps/eeprom-emulation-upgrade/app.c b/apps/eeprom-emulation-upgrade/app.c new file mode 100644 index 00000000..edd73e44 --- /dev/null +++ b/apps/eeprom-emulation-upgrade/app.c @@ -0,0 +1,62 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <peripheral_clk_config.h> +#include "watch.h" + +void app_init(void) { +} + +void app_wake_from_backup(void) { +} + +void app_setup(void) { +    delay_ms(5000); +    while (!(NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY)); +    uint32_t user_row = (*((uint32_t *)NVMCTRL_AUX0_ADDRESS)); +    uint8_t eeprom = (user_row >> NVMCTRL_FUSES_EEPROM_SIZE_Pos) & 7; +    printf("User row read successfully: 0x%lx\n", user_row); + +    if (eeprom != 1) { +        user_row &= ~NVMCTRL_FUSES_EEPROM_SIZE_Msk; +        user_row |= NVMCTRL_FUSES_EEPROM_SIZE(1); +        if (NVMCTRL->STATUS.reg & NVMCTRL_STATUS_SB) { +            printf("Secured bit was set; cannot perform upgrade.\n"); +            return; +        } +        printf("EEPROM configuration was %d.\nApplying change...\n", eeprom); + +        uint32_t temp = NVMCTRL->CTRLB.reg;                                     // Backup settings +        NVMCTRL->CTRLB.reg = temp | NVMCTRL_CTRLB_CACHEDIS;                     // Disable Cache +        NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK;                             // Clear error flags +        NVMCTRL->ADDR.reg = NVMCTRL_AUX0_ADDRESS / 2;                           // Set address, command will be issued elsewhere +        NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMD_EAR | NVMCTRL_CTRLA_CMDEX_KEY;   // Erase the user page +        while (!(NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY));                // Wait for NVM command to complete +        NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK;                             // Clear error flags +        NVMCTRL->ADDR.reg = NVMCTRL_AUX0_ADDRESS / 2;                           // Set address, command will be issued elsewhere +        NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMD_PBC | NVMCTRL_CTRLA_CMDEX_KEY;   // Erase the page buffer before buffering new data +        while (!(NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY));                // Wait for NVM command to complete +        NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK;                             // Clear error flags +        NVMCTRL->ADDR.reg = NVMCTRL_AUX0_ADDRESS / 2;                           // Set address, command will be issued elsewhere +        *((uint32_t *)NVMCTRL_AUX0_ADDRESS) = user_row;                         // write the new fuse values to the memory buffer +        NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMD_WAP | NVMCTRL_CTRLA_CMDEX_KEY;   // Write the user page +        NVMCTRL->CTRLB.reg = temp;                                              // Restore settings + +        printf("Done! Resetting...\n"); +        delay_ms(1000); +        NVIC_SystemReset(); +    } else { +        printf("EEPROM configuration was %d (8192 bytes). Upgrade successful!\n", eeprom); +    } +    printf("%d %d\n", eeprom, NVMCTRL->PARAM.bit.RWWEEP); +} + +void app_prepare_for_standby(void) { +} + +void app_wake_from_standby(void) { +} + +bool app_loop(void) { +    return true; +} diff --git a/apps/flash-test/Makefile b/apps/flash-test/Makefile new file mode 100755 index 00000000..1e552265 --- /dev/null +++ b/apps/flash-test/Makefile @@ -0,0 +1,13 @@ +TOP = ../.. +include $(TOP)/make.mk + +INCLUDES += \ +  -I$(TOP)/littlefs/ \ +  -I./ + +SRCS += \ +  $(TOP)/littlefs/lfs.c \ +  $(TOP)/littlefs/lfs_util.c \ +  ./app.c + +include $(TOP)/rules.mk diff --git a/apps/flash-test/app.c b/apps/flash-test/app.c new file mode 100644 index 00000000..17b1d0aa --- /dev/null +++ b/apps/flash-test/app.c @@ -0,0 +1,131 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <peripheral_clk_config.h> +#include "watch.h" +#include "lfs.h" +#include "hpl_flash.h" + +int lfs_storage_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size); +int lfs_storage_prog(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size); +int lfs_storage_erase(const struct lfs_config *cfg, lfs_block_t block); +int lfs_storage_sync(const struct lfs_config *cfg); + +int lfs_storage_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) { +    (void) cfg; +    return !watch_storage_read(block, off, (void *)buffer, size); +} + +int lfs_storage_prog(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) { +    (void) cfg; +    return !watch_storage_write(block, off, (void *)buffer, size); +} + +int lfs_storage_erase(const struct lfs_config *cfg, lfs_block_t block) { +    (void) cfg; +    return !watch_storage_erase(block); +} + +int lfs_storage_sync(const struct lfs_config *cfg) { +    (void) cfg; +    return !watch_storage_sync(); +} + +const struct lfs_config cfg = { +    // block device operations +    .read  = lfs_storage_read, +    .prog  = lfs_storage_prog, +    .erase = lfs_storage_erase, +    .sync  = lfs_storage_sync, + +    // block device configuration +    .read_size = 16, +    .prog_size = NVMCTRL_PAGE_SIZE, +    .block_size = NVMCTRL_ROW_SIZE, +    .block_count = NVMCTRL_RWWEE_PAGES / 4, +    .cache_size = NVMCTRL_PAGE_SIZE, +    .lookahead_size = 16, +    .block_cycles = 100, +}; + +lfs_t lfs; +lfs_file_t file; + +static int _traverse_df_cb(void *p, lfs_block_t block){ +    (void) block; +	uint32_t *nb = p; +	*nb += 1; +	return 0; +} + +static int get_free_space(void){ +	int err; + +	uint32_t free_blocks = 0; +	err = lfs_fs_traverse(&lfs, _traverse_df_cb, &free_blocks); +	if(err < 0){ +		return err; +	} + +	uint32_t available = cfg.block_count * cfg.block_size - free_blocks * cfg.block_size; + +	return available; +} + +static void cb_tick(void) { +    watch_date_time date_time = watch_rtc_get_date_time(); +    if (date_time.unit.second == 0) { +        int err = lfs_mount(&lfs, &cfg); +        if (err) { +            printf("Mount failed: %d\n", err); +        } +        // read current count +        uint32_t loop_count = 0; +        lfs_file_open(&lfs, &file, "loop_count", LFS_O_RDWR | LFS_O_CREAT); +        lfs_file_read(&lfs, &file, &loop_count, sizeof(loop_count)); + +        // update loop count +        loop_count += 1; +        lfs_file_rewind(&lfs, &file); +        lfs_file_write(&lfs, &file, &loop_count, sizeof(loop_count)); + +        // remember the storage is not updated until the file is closed successfully +        lfs_file_close(&lfs, &file); + +        // release any resources we were using +        lfs_unmount(&lfs); + +        // print the boot count +        printf("loop_count: %ld\n", loop_count); +        printf("free space: %d\n", get_free_space()); +    } +} + +void app_init(void) { +} + +void app_wake_from_backup(void) { +} + +void app_setup(void) { +    // mount the filesystem +    int err = lfs_mount(&lfs, &cfg); + +    // reformat if we can't mount the filesystem +    // this should only happen on the first boot +    if (err) { +        lfs_format(&lfs, &cfg); +        lfs_mount(&lfs, &cfg); +    } +    watch_rtc_register_tick_callback(cb_tick); +} + +void app_prepare_for_standby(void) { +} + +void app_wake_from_standby(void) { +} + +bool app_loop(void) { +    return true; +} @@ -87,6 +87,7 @@ SRCS += \    $(TOP)/watch-library/hardware/watch/watch_i2c.c \    $(TOP)/watch-library/hardware/watch/watch_spi.c \    $(TOP)/watch-library/hardware/watch/watch_uart.c \ +  $(TOP)/watch-library/hardware/watch/watch_storage.c \    $(TOP)/watch-library/hardware/watch/watch_deepsleep.c \    $(TOP)/watch-library/hardware/watch/watch_private.c \    $(TOP)/watch-library/hardware/watch/watch.c \ @@ -158,6 +159,7 @@ SRCS += \    $(TOP)/watch-library/simulator/watch/watch_i2c.c \    $(TOP)/watch-library/simulator/watch/watch_spi.c \    $(TOP)/watch-library/simulator/watch/watch_uart.c \ +  $(TOP)/watch-library/simulator/watch/watch_storage.c \    $(TOP)/watch-library/simulator/watch/watch_deepsleep.c \    $(TOP)/watch-library/simulator/watch/watch_private.c \    $(TOP)/watch-library/simulator/watch/watch.c \ diff --git a/watch-library/hardware/watch/watch_storage.c b/watch-library/hardware/watch/watch_storage.c new file mode 100644 index 00000000..0cd04483 --- /dev/null +++ b/watch-library/hardware/watch/watch_storage.c @@ -0,0 +1,94 @@ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "watch_storage.h" + +#define RWWEE_ADDR_START NVMCTRL_RWW_EEPROM_ADDR +#define RWWEE_ADDR_END (NVMCTRL_RWW_EEPROM_ADDR + NVMCTRL_PAGE_SIZE * NVMCTRL_RWWEE_PAGES) +#define NVM_MEMORY ((volatile uint16_t *)FLASH_ADDR) + +static bool _is_valid_address(uint32_t addr, uint32_t size) { +    if ((addr < NVMCTRL_RWW_EEPROM_ADDR) || (addr > (NVMCTRL_RWW_EEPROM_ADDR + NVMCTRL_PAGE_SIZE * NVMCTRL_RWWEE_PAGES))) { +        return false; +    } +    if ((addr + size > (NVMCTRL_RWW_EEPROM_ADDR + NVMCTRL_PAGE_SIZE * NVMCTRL_RWWEE_PAGES))) { +        return false; +    } + +    return true; +} + +bool watch_storage_read(uint32_t row, uint32_t offset, uint8_t *buffer, uint32_t size) { +    uint32_t address = RWWEE_ADDR_START + row * NVMCTRL_ROW_SIZE + offset; +    if (!_is_valid_address(address, size)) return false; + +    uint32_t nvm_address = address / 2; +    uint32_t i; +    uint16_t data; + +    watch_storage_sync(); + +    if (address % 2) { +        data      = NVM_MEMORY[nvm_address++]; +        buffer[0] = data >> 8; +        i         = 1; +    } else { +        i = 0; +    } + +    while (i < size) { +        data = NVM_MEMORY[nvm_address++]; +        buffer[i] = (data & 0xFF); +        if (i < (size - 1)) { +            buffer[i + 1] = (data >> 8); +        } +        i += 2; +    } +    return true; +} + +bool watch_storage_write(uint32_t row, uint32_t offset, const uint8_t *buffer, uint32_t size) { +    uint32_t address = RWWEE_ADDR_START + row * NVMCTRL_ROW_SIZE + offset; +    if (!_is_valid_address(address, size)) return false; + +    watch_storage_sync(); + +	uint32_t nvm_address = address / 2; +	uint16_t i, data; + +	hri_nvmctrl_write_CTRLA_reg(NVMCTRL, NVMCTRL_CTRLA_CMD_PBC | NVMCTRL_CTRLA_CMDEX_KEY); +    watch_storage_sync(); + +	for (i = 0; i < size; i += 2) { +		data = buffer[i]; +		if (i < NVMCTRL_PAGE_SIZE - 1) { +			data |= (buffer[i + 1] << 8); +		} +		NVM_MEMORY[nvm_address++] = data; +	} +	hri_nvmctrl_write_ADDR_reg(NVMCTRL, address / 2); +	hri_nvmctrl_write_CTRLA_reg(NVMCTRL, NVMCTRL_CTRLA_CMD_RWWEEWP | NVMCTRL_CTRLA_CMDEX_KEY); + +    return true; +} + +bool watch_storage_erase(uint32_t row) { +    uint32_t address = RWWEE_ADDR_START + row * NVMCTRL_ROW_SIZE; +    if (!_is_valid_address(address, NVMCTRL_ROW_SIZE)) return false; + +    watch_storage_sync(); +    hri_nvmctrl_write_ADDR_reg(NVMCTRL, address / 2); +    hri_nvmctrl_write_CTRLA_reg(NVMCTRL, NVMCTRL_CTRLA_CMD_RWWEEER | NVMCTRL_CTRLA_CMDEX_KEY); + +    return true; +} + +bool watch_storage_sync(void) { +    while (!hri_nvmctrl_get_interrupt_READY_bit(NVMCTRL)) { +        // wait for flash to become ready +    } + +    hri_nvmctrl_clear_STATUS_reg(NVMCTRL, NVMCTRL_STATUS_MASK); + +    return true; +} diff --git a/watch-library/shared/watch/watch.h b/watch-library/shared/watch/watch.h index ce85eed3..1dd8e7f7 100644 --- a/watch-library/shared/watch/watch.h +++ b/watch-library/shared/watch/watch.h @@ -64,6 +64,7 @@  #include "watch_i2c.h"  #include "watch_spi.h"  #include "watch_uart.h" +#include "watch_storage.h"  #include "watch_deepsleep.h"  #include "watch_private.h" diff --git a/watch-library/shared/watch/watch_storage.h b/watch-library/shared/watch/watch_storage.h new file mode 100644 index 00000000..6026cbcf --- /dev/null +++ b/watch-library/shared/watch/watch_storage.h @@ -0,0 +1,92 @@ +/* + * MIT License + * + * Copyright (c) 2020 Joey Castillo + * + * 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. + */ +#ifndef _WATCH_STORAGE_H_INCLUDED +#define _WATCH_STORAGE_H_INCLUDED +////< @file watch_storage.h + +#include "watch.h" + +#ifndef NVMCTRL_ROW_SIZE +#define NVMCTRL_ROW_SIZE 256 +#endif +#ifndef NVMCTRL_PAGE_SIZE +#define NVMCTRL_PAGE_SIZE 64 +#endif +#ifndef NVMCTRL_RWWEE_PAGES +#define NVMCTRL_RWWEE_PAGES 128 +#endif + +/** @addtogroup storage Flash Storage +  * @brief This section covers functions related to the SAM L22's 8 kilobyte EEPROM emulation area. +  * @details The SAM L22 inside Sensor Watch has a 256 kilobyte Flash memory array that can be +  *          programmed with whatever data we want. We use most of it to store the bootloader +  *          and the application code that runs on your wrist. The bootloader region is read-only, +  *          and the main application area is only writable by the bootloader (when you drag new +  *          code onto the WATCHBOOT drive). However! there's also a special 8 kilobyte region +  *          at the end of the Flash memory called the EEPROM Emulation Area. This EEPROM emulation +  *          area can be written or erased while the main Flash array is being read. This makes it +  *          super easy to work with, and useful for storing a small amount of non-volatile data that +  *          persists across reboots, even when power is lost. +  *          The functions in this section are very basic, and only cover reading and writing data +  *          in this area. The region is laid out as 32 rows consisting of 4 pages of 64 bytes. +  *          32*4*64 = 8192 bytes. The area can be written one page at a time, but it can only be +  *          erased one row at a time. You can read at arbitrary word-aligned offsets within a row. +  * +  *                 ┌──────────────┬──────────────┬──────────────┬──────────────┐ +  *          Row 0  │   64 bytes   │   64 bytes   │   64 bytes   │   64 bytes   │ +  *                 ├──────────────┼──────────────┼──────────────┼──────────────┤ +  *          Row 1  │   64 bytes   │   64 bytes   │   64 bytes   │   64 bytes   │ +  *                 ├──────────────┼──────────────┼──────────────┼──────────────┤ +  *           ...   │              │              │              │              │ +  *                 ├──────────────┼──────────────┼──────────────┼──────────────┤ +  *          Row 31 │   64 bytes   │   64 bytes   │   64 bytes   │   64 bytes   │ +  *                 └──────────────┴──────────────┴──────────────┴──────────────┘ +  */ +/// @{ +/** @brief Reads a range of bytes from the storage area. +  * @param row The row you want to read. +  * @param offset The offset from the beginning of the row. +  * @param buffer A buffer of at least `size` bytes. +  * @param size The number of bytes you wish to read. +  */ +bool watch_storage_read(uint32_t row, uint32_t offset, uint8_t *buffer, uint32_t size); + +/** @brief Writes bytes to a page in the storage area. Note that the row should already be erased before writing. +  * @param row The row containing the page you want to write. +  * @param offset The offset from the beginning of the row. Must be a multiple of 64. +  * @param buffer The buffer containing the bytes you wish to set. +  * @param size The number of bytes you wish to write. +  */ +bool watch_storage_write(uint32_t row, uint32_t offset, const uint8_t *buffer, uint32_t size); + +/** @brief Erases a row in the storage area, setting all its bytes to 0xFF. +  * @param row The row you want to erase. +  */ +bool watch_storage_erase(uint32_t row); + +/** @brief Waits for any pending writes to complete. +  */ +bool watch_storage_sync(void); +/// @} +#endif diff --git a/watch-library/simulator/watch/watch_storage.c b/watch-library/simulator/watch/watch_storage.c new file mode 100644 index 00000000..27011807 --- /dev/null +++ b/watch-library/simulator/watch/watch_storage.c @@ -0,0 +1,32 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "watch_storage.h" + +uint8_t storage[NVMCTRL_ROW_SIZE * NVMCTRL_RWWEE_PAGES]; + +bool watch_storage_read(uint32_t row, uint32_t offset, uint8_t *buffer, uint32_t size) { +    // printf("read row %ld offset %ld size %ld\n", row, offset, size); +    memcpy(buffer, storage + row * NVMCTRL_ROW_SIZE + offset, size); + +    return true; +} + +bool watch_storage_write(uint32_t row, uint32_t offset, const uint8_t *buffer, uint32_t size) { +    // printf("write row %ld offset %ld size %ld\n", row, offset, size); +    memcpy(storage + row * NVMCTRL_ROW_SIZE + offset, buffer, size); + +    return true; +} + +bool watch_storage_erase(uint32_t row) { +    // printf("erase row %ld\n", row); +    memset(storage + row * NVMCTRL_ROW_SIZE, 0xff, NVMCTRL_ROW_SIZE); + +    return true; +} + +bool watch_storage_sync(void) { +    // nothing to do here! +    return true; +} | 
