mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-07-15 08:32:42 +08:00
Merge branch 'feature/add_wear_levelling' into 'master'
feat(wear_levelling): add wear_levelling component See merge request sdk/ESP8266_RTOS_SDK!1313
This commit is contained in:
6
components/wear_levelling/.gitignore
vendored
Normal file
6
components/wear_levelling/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
**/*.gcno
|
||||
**/*.gcda
|
||||
test_wl_host/coverage_report
|
||||
test_wl_host/coverage.info
|
||||
**/*.o
|
||||
test_wl_host/test_wl
|
10
components/wear_levelling/CMakeLists.txt
Normal file
10
components/wear_levelling/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
idf_component_register(SRCS "Partition.cpp"
|
||||
"SPI_Flash.cpp"
|
||||
"WL_Ext_Perf.cpp"
|
||||
"WL_Ext_Safe.cpp"
|
||||
"WL_Flash.cpp"
|
||||
"crc32.cpp"
|
||||
"wear_levelling.cpp"
|
||||
INCLUDE_DIRS include
|
||||
PRIV_INCLUDE_DIRS private_include
|
||||
REQUIRES spi_flash)
|
62
components/wear_levelling/Kconfig
Normal file
62
components/wear_levelling/Kconfig
Normal file
@ -0,0 +1,62 @@
|
||||
menu "Wear Levelling"
|
||||
|
||||
choice WL_SECTOR_SIZE
|
||||
bool "Wear Levelling library sector size"
|
||||
default WL_SECTOR_SIZE_4096
|
||||
help
|
||||
Sector size used by wear levelling library.
|
||||
You can set default sector size or size that will
|
||||
fit to the flash device sector size.
|
||||
|
||||
With sector size set to 4096 bytes, wear levelling library is more
|
||||
efficient. However if FAT filesystem is used on top of wear levelling
|
||||
library, it will need more temporary storage: 4096 bytes for each
|
||||
mounted filesystem and 4096 bytes for each opened file.
|
||||
|
||||
With sector size set to 512 bytes, wear levelling library will perform
|
||||
more operations with flash memory, but less RAM will be used by FAT
|
||||
filesystem library (512 bytes for the filesystem and 512 bytes for each
|
||||
file opened).
|
||||
|
||||
config WL_SECTOR_SIZE_512
|
||||
bool "512"
|
||||
config WL_SECTOR_SIZE_4096
|
||||
bool "4096"
|
||||
endchoice
|
||||
|
||||
config WL_SECTOR_SIZE
|
||||
int
|
||||
default 512 if WL_SECTOR_SIZE_512
|
||||
default 4096 if WL_SECTOR_SIZE_4096
|
||||
|
||||
choice WL_SECTOR_MODE
|
||||
bool "Sector store mode"
|
||||
depends on WL_SECTOR_SIZE_512
|
||||
default WL_SECTOR_MODE_SAFE
|
||||
help
|
||||
Specify the mode to store data into flash:
|
||||
|
||||
- In Performance mode a data will be stored to the RAM and then
|
||||
stored back to the flash. Compared to the Safety mode, this operation is
|
||||
faster, but if power will be lost when erase sector operation is in
|
||||
progress, then the data from complete flash device sector will be lost.
|
||||
|
||||
- In Safety mode data from complete flash device sector will be read from
|
||||
flash, modified, and then stored back to flash.
|
||||
Compared to the Performance mode, this operation is slower, but if
|
||||
power is lost during erase sector operation, then the data from full
|
||||
flash device sector will not be lost.
|
||||
|
||||
config WL_SECTOR_MODE_PERF
|
||||
bool "Perfomance"
|
||||
|
||||
config WL_SECTOR_MODE_SAFE
|
||||
bool "Safety"
|
||||
endchoice
|
||||
|
||||
config WL_SECTOR_MODE
|
||||
int
|
||||
default 0 if WL_SECTOR_MODE_PERF
|
||||
default 1 if WL_SECTOR_MODE_SAFE
|
||||
|
||||
endmenu
|
68
components/wear_levelling/Partition.cpp
Normal file
68
components/wear_levelling/Partition.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "Partition.h"
|
||||
static const char *TAG = "wl_partition";
|
||||
|
||||
Partition::Partition(const esp_partition_t *partition)
|
||||
{
|
||||
this->partition = partition;
|
||||
}
|
||||
|
||||
size_t Partition::chip_size()
|
||||
{
|
||||
return this->partition->size;
|
||||
}
|
||||
|
||||
esp_err_t Partition::erase_sector(size_t sector)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
result = erase_range(sector * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t Partition::erase_range(size_t start_address, size_t size)
|
||||
{
|
||||
esp_err_t result = esp_partition_erase_range(this->partition, start_address, size);
|
||||
if (result == ESP_OK) {
|
||||
ESP_LOGV(TAG, "erase_range - start_address=0x%08x, size=0x%08x, result=0x%08x", start_address, size, result);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "erase_range - start_address=0x%08x, size=0x%08x, result=0x%08x", start_address, size, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t Partition::write(size_t dest_addr, const void *src, size_t size)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
result = esp_partition_write(this->partition, dest_addr, src, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t Partition::read(size_t src_addr, void *dest, size_t size)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
result = esp_partition_read(this->partition, src_addr, dest, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t Partition::sector_size()
|
||||
{
|
||||
return SPI_FLASH_SEC_SIZE;
|
||||
}
|
||||
|
||||
Partition::~Partition()
|
||||
{
|
||||
|
||||
}
|
49
components/wear_levelling/README.rst
Normal file
49
components/wear_levelling/README.rst
Normal file
@ -0,0 +1,49 @@
|
||||
Wear Levelling API
|
||||
==================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Overview
|
||||
--------
|
||||
Most of flash memory and especially SPI flash that is used in ESP32 has a sector-based organization and also has a limited number of erase/modification cycles per memory sector. The wear levelling component helps to distribute wear and tear among sectors more evenly without requiring any attention from the user.
|
||||
|
||||
The wear levelling component provides API functions related to reading, writing, erasing, and memory mapping of data in external SPI flash through the partition component. The component also has higher-level API functions which work with the FAT filesystem defined in :doc:`FAT filesystem </api-reference/storage/fatfs>`.
|
||||
|
||||
The wear levelling component, together with the FAT FS component, uses FAT FS sectors of 4096 bytes, which is a standard size for flash memory. With this size, the component shows the best performance but needs additional memory in RAM.
|
||||
|
||||
To save internal memory, the component has two additional modes which both use sectors of 512 bytes:
|
||||
|
||||
- **Performance mode.** Erase sector operation data is stored in RAM, the sector is erased, and then data is copied back to flash memory. However, if a device is powered off for any reason, all 4096 bytes of data is lost.
|
||||
- **Safety mode.** The data is first saved to flash memory, and after the sector is erased, the data is saved back. If a device is powered off, the data can be recovered as soon as the device boots up.
|
||||
|
||||
The default settings are as follows:
|
||||
- Sector size is 512 bytes
|
||||
- Performance mode
|
||||
|
||||
You can change the settings through the configuration menu.
|
||||
|
||||
|
||||
The wear levelling component does not cache data in RAM. The write and erase functions modify flash directly, and flash contents are consistent when the function returns.
|
||||
|
||||
|
||||
Wear Levelling access API functions
|
||||
-----------------------------------
|
||||
|
||||
This is the set of API functions for working with data in flash:
|
||||
|
||||
- ``wl_mount`` - initializes the wear levelling module and mounts the specified partition
|
||||
- ``wl_unmount`` - unmounts the partition and deinitializes the wear levelling module
|
||||
- ``wl_erase_range`` - erases a range of addresses in flash
|
||||
- ``wl_write`` - writes data to a partition
|
||||
- ``wl_read`` - reads data from a partition
|
||||
- ``wl_size`` - returns the size of available memory in bytes
|
||||
- ``wl_sector_size`` - returns the size of one sector
|
||||
|
||||
As a rule, try to avoid using raw wear levelling functions and use filesystem-specific functions instead.
|
||||
|
||||
|
||||
Memory Size
|
||||
-----------
|
||||
|
||||
The memory size is calculated in the wear levelling module based on partition parameters. The module uses some sectors of flash for internal data.
|
||||
|
47
components/wear_levelling/README_CN.rst
Normal file
47
components/wear_levelling/README_CN.rst
Normal file
@ -0,0 +1,47 @@
|
||||
磨损均衡 API
|
||||
==================
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
概述
|
||||
--------
|
||||
|
||||
ESP32 所使用的 flash,特别是 SPI flash 多数具备扇区结构,且每个扇区仅允许有限次数的擦除/修改操作。为了避免过度使用某一扇区,乐鑫提供了磨损均衡组件,无需用户介入即可帮助用户均衡各个扇区之间的磨损。
|
||||
|
||||
磨损均衡组件包含了通过分区组件对外部 SPI flash 进行数据读取、写入、擦除和存储器映射相关的 API 函数。磨损均衡组件还具有软件上更高级别的 API 函数,与 :doc:`FAT 文件系统 </api-reference/storage/fatfs>` 协同工作。
|
||||
|
||||
磨损均衡组件与 FAT 文件系统组件共用 FAT 文件系统的扇区,扇区大小为 4096 字节,是标准 flash 扇区的大小。在这种模式下,磨损均衡组件性能达到最佳,但需要在 RAM 中占用更多内存。
|
||||
|
||||
为了节省内存,磨损均衡组件还提供了另外两种模式,均使用 512 字节大小的扇区:
|
||||
|
||||
- **性能模式**:先将数据保存在 RAM 中,擦除扇区,然后将数据存储回 flash。如果设备在扇区擦写过程中突然断电,则整个扇区(4096 字节)数据将全部丢失。
|
||||
- **安全模式**:数据先保存在 flash 中空余扇区,擦除扇区后,数据即存储回去。如果设备断电,上电后可立即恢复数据。
|
||||
|
||||
设备默认设置如下:
|
||||
|
||||
- 定义扇区大小为 512 字节
|
||||
- 默认使用性能模式
|
||||
|
||||
您可以使用配置菜单更改设置。
|
||||
|
||||
磨损均衡组件不会将数据缓存在 RAM 中。写入和擦除函数直接修改 flash,函数返回后,flash 即完成修改。
|
||||
|
||||
磨损均衡访问 API
|
||||
-----------------------------------
|
||||
|
||||
处理 flash 数据常用的 API 如下所示:
|
||||
|
||||
- ``wl_mount`` - 为指定分区挂载并初始化磨损均衡模块
|
||||
- ``wl_unmount`` - 卸载分区并释放磨损均衡模块
|
||||
- ``wl_erase_range`` - 擦除 flash 中指定的地址范围
|
||||
- ``wl_write`` - 将数据写入分区
|
||||
- ``wl_read`` - 从分区读取数据
|
||||
- ``wl_size`` - 返回可用内存的大小(以字节为单位)
|
||||
- ``wl_sector_size`` - 返回一个扇区的大小
|
||||
|
||||
请尽量避免直接使用原始磨损均衡函数,建议您使用文件系统特定的函数。
|
||||
|
||||
内存大小
|
||||
-----------
|
||||
|
||||
内存大小是根据分区参数在磨损均衡模块中计算所得,由于模块使用 flash 部分扇区存储内部数据,因此计算所得内存大小有少许偏差。
|
81
components/wear_levelling/SPI_Flash.cpp
Normal file
81
components/wear_levelling/SPI_Flash.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "SPI_Flash.h"
|
||||
#include "esp_spi_flash.h"
|
||||
static const char *TAG = "spi_flash";
|
||||
|
||||
SPI_Flash::SPI_Flash()
|
||||
{
|
||||
}
|
||||
|
||||
size_t SPI_Flash::chip_size()
|
||||
{
|
||||
return spi_flash_get_chip_size();
|
||||
}
|
||||
|
||||
esp_err_t SPI_Flash::erase_sector(size_t sector)
|
||||
{
|
||||
esp_err_t result = spi_flash_erase_sector(sector);
|
||||
if (result == ESP_OK) {
|
||||
ESP_LOGV(TAG, "erase_sector - sector=0x%08x, result=0x%08x", sector, result);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "erase_sector - sector=0x%08x, result=0x%08x", sector, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
esp_err_t SPI_Flash::erase_range(size_t start_address, size_t size)
|
||||
{
|
||||
size = (size + SPI_FLASH_SEC_SIZE - 1) / SPI_FLASH_SEC_SIZE;
|
||||
size = size * SPI_FLASH_SEC_SIZE;
|
||||
esp_err_t result = spi_flash_erase_range(start_address, size);
|
||||
if (result == ESP_OK) {
|
||||
ESP_LOGV(TAG, "erase_range - start_address=0x%08x, size=0x%08x, result=0x%08x", start_address, size, result);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "erase_range - start_address=0x%08x, size=0x%08x, result=0x%08x", start_address, size, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t SPI_Flash::write(size_t dest_addr, const void *src, size_t size)
|
||||
{
|
||||
esp_err_t result = spi_flash_write(dest_addr, src, size);
|
||||
if (result == ESP_OK) {
|
||||
ESP_LOGV(TAG, "write - dest_addr=0x%08x, size=0x%08x, result=0x%08x", dest_addr, size, result);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "write - dest_addr=0x%08x, size=0x%08x, result=0x%08x", dest_addr, size, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t SPI_Flash::read(size_t src_addr, void *dest, size_t size)
|
||||
{
|
||||
esp_err_t result = spi_flash_read(src_addr, dest, size);
|
||||
if (result == ESP_OK) {
|
||||
ESP_LOGV(TAG, "read - src_addr=0x%08x, size=0x%08x, result=0x%08x", src_addr, size, result);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "read - src_addr=0x%08x, size=0x%08x, result=0x%08x", src_addr, size, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t SPI_Flash::sector_size()
|
||||
{
|
||||
return SPI_FLASH_SEC_SIZE;
|
||||
}
|
||||
|
||||
SPI_Flash::~SPI_Flash()
|
||||
{
|
||||
}
|
168
components/wear_levelling/WL_Ext_Perf.cpp
Normal file
168
components/wear_levelling/WL_Ext_Perf.cpp
Normal file
@ -0,0 +1,168 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "WL_Ext_Perf.h"
|
||||
#include <stdlib.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "wl_ext_perf";
|
||||
|
||||
#define WL_EXT_RESULT_CHECK(result) \
|
||||
if (result != ESP_OK) { \
|
||||
ESP_LOGE(TAG,"%s(%d): result = 0x%08x", __FUNCTION__, __LINE__, result); \
|
||||
return (result); \
|
||||
}
|
||||
|
||||
WL_Ext_Perf::WL_Ext_Perf(): WL_Flash()
|
||||
{
|
||||
this->sector_buffer = NULL;
|
||||
}
|
||||
|
||||
WL_Ext_Perf::~WL_Ext_Perf()
|
||||
{
|
||||
free(this->sector_buffer);
|
||||
}
|
||||
|
||||
esp_err_t WL_Ext_Perf::config(WL_Config_s *cfg, Flash_Access *flash_drv)
|
||||
{
|
||||
wl_ext_cfg_t *config = (wl_ext_cfg_t *)cfg;
|
||||
|
||||
this->fat_sector_size = config->fat_sector_size;
|
||||
this->flash_sector_size = cfg->sector_size;
|
||||
|
||||
this->sector_buffer = (uint32_t *)malloc(cfg->sector_size);
|
||||
if (this->sector_buffer == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
this->size_factor = this->flash_sector_size / this->fat_sector_size;
|
||||
if (this->size_factor < 1) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return WL_Flash::config(cfg, flash_drv);
|
||||
}
|
||||
|
||||
esp_err_t WL_Ext_Perf::init()
|
||||
{
|
||||
return WL_Flash::init();
|
||||
}
|
||||
|
||||
size_t WL_Ext_Perf::chip_size()
|
||||
{
|
||||
return WL_Flash::chip_size();
|
||||
}
|
||||
size_t WL_Ext_Perf::sector_size()
|
||||
{
|
||||
return this->fat_sector_size;
|
||||
}
|
||||
|
||||
esp_err_t WL_Ext_Perf::erase_sector(size_t sector)
|
||||
{
|
||||
return this->erase_sector_fit(sector, 1);
|
||||
}
|
||||
|
||||
esp_err_t WL_Ext_Perf::erase_sector_fit(uint32_t start_sector, uint32_t count)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s begin, start_sector = 0x%08x, count = %i", __func__, start_sector, count);
|
||||
// This method works with one flash device sector and able to erase "count" of fatfs sectors from this sector
|
||||
esp_err_t result = ESP_OK;
|
||||
|
||||
uint32_t pre_check_start = start_sector % this->size_factor;
|
||||
|
||||
|
||||
for (int i = 0; i < this->size_factor; i++) {
|
||||
if ((i < pre_check_start) || (i >= count + pre_check_start)) {
|
||||
result = this->read(start_sector / this->size_factor * this->flash_sector_size + i * this->fat_sector_size, &this->sector_buffer[i * this->fat_sector_size / sizeof(uint32_t)], this->fat_sector_size);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
}
|
||||
}
|
||||
|
||||
result = WL_Flash::erase_sector(start_sector / this->size_factor); // erase comlete flash sector
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
// And write back only data that should not be erased...
|
||||
for (int i = 0; i < this->size_factor; i++) {
|
||||
if ((i < pre_check_start) || (i >= count + pre_check_start)) {
|
||||
result = this->write(start_sector / this->size_factor * this->flash_sector_size + i * this->fat_sector_size, &this->sector_buffer[i * this->fat_sector_size / sizeof(uint32_t)], this->fat_sector_size);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t WL_Ext_Perf::erase_range(size_t start_address, size_t size)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
if ((start_address % this->fat_sector_size) != 0) {
|
||||
result = ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (((size % this->fat_sector_size) != 0) || (size == 0)) {
|
||||
result = ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
|
||||
// The range to erase could be allocated in any possible way
|
||||
// ---------------------------------------------------------
|
||||
// | | | | |
|
||||
// |0|0|x|x|x|x|x|x|x|x|x|x|x|x|0|0|
|
||||
// | pre | rest | rest | post | <- check ranges
|
||||
//
|
||||
// Pre check - the data that is not fit to the full sector at the begining of the erased block
|
||||
// Post check - the data that are not fit to the full sector at the end of the erased block
|
||||
// rest - data that are fit to the flash device sector at the middle of the erased block
|
||||
//
|
||||
// In case of pre and post check situations the data of the non erased area have to be readed first and then
|
||||
// stored back.
|
||||
// For the rest area this operation not needed because complete flash device sector will be erased.
|
||||
|
||||
ESP_LOGV(TAG, "%s begin, addr = 0x%08x, size = %i", __func__, start_address, size);
|
||||
// Calculate pre check values
|
||||
uint32_t pre_check_start = (start_address / this->fat_sector_size) % this->size_factor;
|
||||
uint32_t sectors_count = size / this->fat_sector_size;
|
||||
uint32_t pre_check_count = (this->size_factor - pre_check_start);
|
||||
if (pre_check_count > sectors_count) {
|
||||
pre_check_count = sectors_count;
|
||||
}
|
||||
|
||||
// Calculate post ckeck
|
||||
uint32_t post_check_count = (sectors_count - pre_check_count) % this->size_factor;
|
||||
uint32_t post_check_start = ((start_address + size - post_check_count * this->fat_sector_size) / this->fat_sector_size);
|
||||
|
||||
// Calculate rest
|
||||
uint32_t rest_check_count = sectors_count - pre_check_count - post_check_count;
|
||||
if ((pre_check_count == this->size_factor) && (0 == pre_check_start)) {
|
||||
rest_check_count+=this->size_factor;
|
||||
pre_check_count = 0;
|
||||
}
|
||||
uint32_t rest_check_start = start_address + pre_check_count * this->fat_sector_size;
|
||||
|
||||
// Here we will clear pre_check_count amount of sectors
|
||||
if (pre_check_count != 0) {
|
||||
result = this->erase_sector_fit(start_address / this->fat_sector_size, pre_check_count);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
}
|
||||
ESP_LOGV(TAG, "%s rest_check_start = %i, pre_check_count=%i, rest_check_count=%i, post_check_count=%i\n", __func__, rest_check_start, pre_check_count, rest_check_count, post_check_count);
|
||||
if (rest_check_count > 0) {
|
||||
rest_check_count = rest_check_count / this->size_factor;
|
||||
size_t start_sector = rest_check_start / this->flash_sector_size;
|
||||
for (size_t i = 0; i < rest_check_count; i++) {
|
||||
result = WL_Flash::erase_sector(start_sector + i);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
}
|
||||
}
|
||||
if (post_check_count != 0) {
|
||||
result = this->erase_sector_fit(post_check_start, post_check_count);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
161
components/wear_levelling/WL_Ext_Safe.cpp
Normal file
161
components/wear_levelling/WL_Ext_Safe.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "WL_Ext_Safe.h"
|
||||
#include <stdlib.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "wl_ext_safe";
|
||||
|
||||
#define WL_EXT_RESULT_CHECK(result) \
|
||||
if (result != ESP_OK) { \
|
||||
ESP_LOGE(TAG,"%s(%d): result = 0x%08x", __FUNCTION__, __LINE__, result); \
|
||||
return (result); \
|
||||
}
|
||||
|
||||
#ifndef FLASH_ERASE_VALUE
|
||||
#define FLASH_ERASE_VALUE 0xffffffff
|
||||
#endif // FLASH_ERASE_VALUE
|
||||
|
||||
|
||||
#ifndef WL_EXT_SAFE_OK
|
||||
#define WL_EXT_SAFE_OK 0x12345678
|
||||
#endif // WL_EXT_SAFE_OK
|
||||
|
||||
#ifndef WL_EXT_SAFE_OFFSET
|
||||
#define WL_EXT_SAFE_OFFSET 16
|
||||
#endif // WL_EXT_SAFE_OFFSET
|
||||
|
||||
|
||||
struct WL_Ext_Safe_State {
|
||||
public:
|
||||
uint32_t erase_begin;
|
||||
uint32_t local_addr_base;
|
||||
uint32_t local_addr_shift;
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
WL_Ext_Safe::WL_Ext_Safe(): WL_Ext_Perf()
|
||||
{
|
||||
}
|
||||
|
||||
WL_Ext_Safe::~WL_Ext_Safe()
|
||||
{
|
||||
}
|
||||
|
||||
esp_err_t WL_Ext_Safe::config(WL_Config_s *cfg, Flash_Access *flash_drv)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
|
||||
result = WL_Ext_Perf::config(cfg, flash_drv);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
this->state_addr = WL_Flash::chip_size() - 2 * WL_Flash::sector_size();
|
||||
this->dump_addr = WL_Flash::chip_size() - 1 * WL_Flash::sector_size();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t WL_Ext_Safe::init()
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
|
||||
result = WL_Ext_Perf::init();
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
|
||||
result = this->recover();
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t WL_Ext_Safe::chip_size()
|
||||
{
|
||||
ESP_LOGV(TAG, "%s size = %i", __func__, WL_Flash::chip_size() - 2 * this->flash_sector_size);
|
||||
return WL_Flash::chip_size() - 2 * this->flash_sector_size;
|
||||
}
|
||||
|
||||
esp_err_t WL_Ext_Safe::recover()
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
|
||||
WL_Ext_Safe_State state;
|
||||
result = WL_Flash::read(this->state_addr, &state, sizeof(WL_Ext_Safe_State));
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
ESP_LOGV(TAG, "%s recover, start_addr = 0x%08x, local_addr_base = 0x%08x, local_addr_shift = %i, count=%i", __func__, state.erase_begin, state.local_addr_base, state.local_addr_shift, state.count);
|
||||
|
||||
// check if we have transaction
|
||||
if (state.erase_begin == WL_EXT_SAFE_OK) {
|
||||
|
||||
result = this->read(this->dump_addr, this->sector_buffer, this->flash_sector_size);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
|
||||
result = WL_Flash::erase_sector(state.local_addr_base); // erase comlete flash sector
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
|
||||
// And write back...
|
||||
for (int i = 0; i < this->size_factor; i++) {
|
||||
if ((i < state.local_addr_shift) || (i >= state.count + state.local_addr_shift)) {
|
||||
result = this->write(state.local_addr_base * this->flash_sector_size + i * this->fat_sector_size, &this->sector_buffer[i * this->fat_sector_size / sizeof(uint32_t)], this->fat_sector_size);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
}
|
||||
}
|
||||
// clear transaction
|
||||
result = WL_Flash::erase_range(this->state_addr, this->flash_sector_size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t WL_Ext_Safe::erase_sector_fit(uint32_t start_sector, uint32_t count)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
|
||||
uint32_t local_addr_base = start_sector / this->size_factor;
|
||||
uint32_t pre_check_start = start_sector % this->size_factor;
|
||||
ESP_LOGV(TAG, "%s start_sector=0x%08x, count = %i", __func__, start_sector, count);
|
||||
for (int i = 0; i < this->size_factor; i++) {
|
||||
if ((i < pre_check_start) || (i >= count + pre_check_start)) {
|
||||
result = this->read(start_sector / this->size_factor * this->flash_sector_size + i * this->fat_sector_size, &this->sector_buffer[i * this->fat_sector_size / sizeof(uint32_t)], this->fat_sector_size);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
}
|
||||
}
|
||||
|
||||
result = WL_Flash::erase_sector(this->dump_addr / this->flash_sector_size);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
result = WL_Flash::write(this->dump_addr, this->sector_buffer, this->flash_sector_size);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
|
||||
WL_Ext_Safe_State state;
|
||||
state.erase_begin = WL_EXT_SAFE_OK;
|
||||
state.local_addr_base = local_addr_base;
|
||||
state.local_addr_shift = pre_check_start;
|
||||
state.count = count;
|
||||
|
||||
result = WL_Flash::erase_sector(this->state_addr / this->flash_sector_size);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
result = WL_Flash::write(this->state_addr + 0, &state, sizeof(WL_Ext_Safe_State));
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
|
||||
// Erase
|
||||
result = WL_Flash::erase_sector(local_addr_base); // erase comlete flash sector
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
// And write back...
|
||||
for (int i = 0; i < this->size_factor; i++) {
|
||||
if ((i < pre_check_start) || (i >= count + pre_check_start)) {
|
||||
result = this->write(local_addr_base * this->flash_sector_size + i * this->fat_sector_size, &this->sector_buffer[i * this->fat_sector_size / sizeof(uint32_t)], this->fat_sector_size);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
}
|
||||
}
|
||||
|
||||
result = WL_Flash::erase_sector(this->state_addr / this->flash_sector_size);
|
||||
WL_EXT_RESULT_CHECK(result);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
632
components/wear_levelling/WL_Flash.cpp
Normal file
632
components/wear_levelling/WL_Flash.cpp
Normal file
@ -0,0 +1,632 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "WL_Flash.h"
|
||||
#include <stdlib.h>
|
||||
#include "crc32.h"
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
static const char *TAG = "wl_flash";
|
||||
#ifndef WL_CFG_CRC_CONST
|
||||
#define WL_CFG_CRC_CONST UINT32_MAX
|
||||
#endif // WL_CFG_CRC_CONST
|
||||
|
||||
#define WL_RESULT_CHECK(result) \
|
||||
if (result != ESP_OK) { \
|
||||
ESP_LOGE(TAG,"%s(%d): result = 0x%08x", __FUNCTION__, __LINE__, result); \
|
||||
return (result); \
|
||||
}
|
||||
|
||||
#ifndef _MSC_VER // MSVS has different format for this define
|
||||
static_assert(sizeof(wl_state_t) % 32 == 0, "wl_state_t structure size must be multiple of flash encryption unit size");
|
||||
#endif // _MSC_VER
|
||||
|
||||
|
||||
WL_Flash::WL_Flash()
|
||||
{
|
||||
}
|
||||
|
||||
WL_Flash::~WL_Flash()
|
||||
{
|
||||
free(this->temp_buff);
|
||||
}
|
||||
|
||||
esp_err_t WL_Flash::config(wl_config_t *cfg, Flash_Access *flash_drv)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s start_addr=0x%08x, full_mem_size=0x%08x, page_size=0x%08x, sector_size=0x%08x, updaterate=0x%08x, wr_size=0x%08x, version=0x%08x, temp_buff_size=0x%08x", __func__,
|
||||
(uint32_t) cfg->start_addr,
|
||||
cfg->full_mem_size,
|
||||
cfg->page_size,
|
||||
cfg->sector_size,
|
||||
cfg->updaterate,
|
||||
cfg->wr_size,
|
||||
cfg->version,
|
||||
(uint32_t) cfg->temp_buff_size);
|
||||
|
||||
cfg->crc = crc32::crc32_le(WL_CFG_CRC_CONST, (const unsigned char *)cfg, offsetof(wl_config_t, crc));
|
||||
esp_err_t result = ESP_OK;
|
||||
memcpy(&this->cfg, cfg, sizeof(wl_config_t));
|
||||
if (this->cfg.temp_buff_size < this->cfg.wr_size) {
|
||||
this->cfg.temp_buff_size = this->cfg.wr_size;
|
||||
}
|
||||
this->configured = false;
|
||||
if (cfg == NULL) {
|
||||
result = ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
this->flash_drv = flash_drv;
|
||||
if (flash_drv == NULL) {
|
||||
result = ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if ((this->cfg.sector_size % this->cfg.temp_buff_size) != 0) {
|
||||
result = ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (this->cfg.page_size < this->cfg.sector_size) {
|
||||
result = ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
WL_RESULT_CHECK(result);
|
||||
|
||||
this->state_size = this->cfg.sector_size;
|
||||
if (this->state_size < (sizeof(wl_state_t) + (this->cfg.full_mem_size / this->cfg.sector_size)*this->cfg.wr_size)) {
|
||||
this->state_size = ((sizeof(wl_state_t) + (this->cfg.full_mem_size / this->cfg.sector_size) * this->cfg.wr_size) + this->cfg.sector_size - 1) / this->cfg.sector_size;
|
||||
this->state_size = this->state_size * this->cfg.sector_size;
|
||||
}
|
||||
this->cfg_size = (sizeof(wl_config_t) + this->cfg.sector_size - 1) / this->cfg.sector_size;
|
||||
this->cfg_size = cfg_size * this->cfg.sector_size;
|
||||
|
||||
this->addr_cfg = this->cfg.start_addr + this->cfg.full_mem_size - this->cfg_size;
|
||||
this->addr_state1 = this->cfg.start_addr + this->cfg.full_mem_size - this->state_size * 2 - this->cfg_size; // allocate data at the end of memory
|
||||
this->addr_state2 = this->cfg.start_addr + this->cfg.full_mem_size - this->state_size * 1 - this->cfg_size; // allocate data at the end of memory
|
||||
|
||||
ptrdiff_t flash_sz = ((this->cfg.full_mem_size - this->state_size * 2 - this->cfg_size) / this->cfg.page_size - 1) * this->cfg.page_size; // -1 remove dummy block
|
||||
this->flash_size = ((this->cfg.full_mem_size - this->state_size * 2 - this->cfg_size) / this->cfg.page_size - 1) * this->cfg.page_size; // -1 remove dummy block
|
||||
|
||||
ESP_LOGD(TAG, "%s - config result: state_size=0x%08x, cfg_size=0x%08x, addr_cfg=0x%08x, addr_state1=0x%08x, addr_state2=0x%08x, flash_size=0x%08x", __func__,
|
||||
(uint32_t) this->state_size,
|
||||
(uint32_t) this->cfg_size,
|
||||
(uint32_t) this->addr_cfg,
|
||||
(uint32_t) this->addr_state1,
|
||||
(uint32_t) this->addr_state2,
|
||||
(uint32_t) this->flash_size
|
||||
);
|
||||
if (flash_sz <= 0) {
|
||||
result = ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
WL_RESULT_CHECK(result);
|
||||
|
||||
this->temp_buff = (uint8_t *)malloc(this->cfg.temp_buff_size);
|
||||
if (this->temp_buff == NULL) {
|
||||
result = ESP_ERR_NO_MEM;
|
||||
}
|
||||
WL_RESULT_CHECK(result);
|
||||
this->configured = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t WL_Flash::init()
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
if (this->configured == false) {
|
||||
ESP_LOGW(TAG, "WL_Flash: not configured, call config() first");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
// If flow will be interrupted by error, then this flag will be false
|
||||
this->initialized = false;
|
||||
// Init states if it is first time...
|
||||
this->flash_drv->read(this->addr_state1, &this->state, sizeof(wl_state_t));
|
||||
wl_state_t sa_copy;
|
||||
wl_state_t *state_copy = &sa_copy;
|
||||
result = this->flash_drv->read(this->addr_state2, state_copy, sizeof(wl_state_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
|
||||
int check_size = WL_STATE_CRC_LEN_V2;
|
||||
// Chech CRC and recover state
|
||||
uint32_t crc1 = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, check_size);
|
||||
uint32_t crc2 = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)state_copy, check_size);
|
||||
|
||||
ESP_LOGD(TAG, "%s - config ID=%i, stored ID=%i, access_count=%i, block_size=%i, max_count=%i, pos=%i, move_count=0x%8.8X",
|
||||
__func__,
|
||||
this->cfg.version,
|
||||
this->state.version,
|
||||
this->state.access_count,
|
||||
this->state.block_size,
|
||||
this->state.max_count,
|
||||
this->state.pos,
|
||||
this->state.move_count);
|
||||
|
||||
ESP_LOGD(TAG, "%s starts: crc1= 0x%08x, crc2 = 0x%08x, this->state.crc= 0x%08x, state_copy->crc= 0x%08x, version=%i, read_version=%i", __func__, crc1, crc2, this->state.crc, state_copy->crc, this->cfg.version, this->state.version);
|
||||
if ((crc1 == this->state.crc) && (crc2 == state_copy->crc)) {
|
||||
// The state is OK. Check the ID
|
||||
if (this->state.version != this->cfg.version) {
|
||||
result = this->initSections();
|
||||
WL_RESULT_CHECK(result);
|
||||
result = this->recoverPos();
|
||||
WL_RESULT_CHECK(result);
|
||||
} else {
|
||||
if (crc1 != crc2) {// we did not update second structure.
|
||||
result = this->flash_drv->erase_range(this->addr_state2, this->state_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
result = this->flash_drv->write(this->addr_state2, &this->state, sizeof(wl_state_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
for (size_t i = 0; i < ((this->cfg.full_mem_size / this->cfg.sector_size)*this->cfg.wr_size); i++) {
|
||||
bool pos_bits;
|
||||
result = this->flash_drv->read(this->addr_state1 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
pos_bits = this->OkBuffSet(i);
|
||||
if (pos_bits == true) {
|
||||
//this->fillOkBuff(i);
|
||||
result = this->flash_drv->write(this->addr_state2 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "%s: crc1=0x%08x, crc2 = 0x%08x, result= 0x%08x", __func__, crc1, crc2, (uint32_t)result);
|
||||
result = this->recoverPos();
|
||||
WL_RESULT_CHECK(result);
|
||||
}
|
||||
} else if ((crc1 != this->state.crc) && (crc2 != state_copy->crc)) { // This is just new flash or new version
|
||||
// Check if this is new version or just new instance of WL
|
||||
ESP_LOGD(TAG, "%s: try to update version - crc1= 0x%08x, crc2 = 0x%08x, result= 0x%08x", __func__, (uint32_t)crc1, (uint32_t)crc2, (uint32_t)result);
|
||||
result = this->updateVersion();
|
||||
if (result == ESP_FAIL) {
|
||||
ESP_LOGD(TAG, "%s: init flash sections", __func__);
|
||||
result = this->initSections();
|
||||
WL_RESULT_CHECK(result);
|
||||
}
|
||||
result = this->recoverPos();
|
||||
WL_RESULT_CHECK(result);
|
||||
} else {
|
||||
// recover broken state
|
||||
if (crc1 == this->state.crc) {// we have to recover state 2
|
||||
result = this->flash_drv->erase_range(this->addr_state2, this->state_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
result = this->flash_drv->write(this->addr_state2, &this->state, sizeof(wl_state_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
for (size_t i = 0; i < ((this->cfg.full_mem_size / this->cfg.sector_size) * this->cfg.wr_size); i++) {
|
||||
bool pos_bits;
|
||||
result = this->flash_drv->read(this->addr_state1 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
pos_bits = this->OkBuffSet(i);
|
||||
if (pos_bits == true) {
|
||||
result = this->flash_drv->write(this->addr_state2 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
}
|
||||
}
|
||||
result = this->flash_drv->read(this->addr_state2, &this->state, sizeof(wl_state_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
} else { // we have to recover state 1
|
||||
result = this->flash_drv->erase_range(this->addr_state1, this->state_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
result = this->flash_drv->write(this->addr_state1, state_copy, sizeof(wl_state_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
for (size_t i = 0; i < ((this->cfg.full_mem_size / this->cfg.sector_size) * this->cfg.wr_size); i++) {
|
||||
bool pos_bits;
|
||||
result = this->flash_drv->read(this->addr_state2 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
|
||||
|
||||
WL_RESULT_CHECK(result);
|
||||
pos_bits = this->OkBuffSet(i);
|
||||
if (pos_bits == true) {
|
||||
result = this->flash_drv->write(this->addr_state1 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
}
|
||||
}
|
||||
result = this->flash_drv->read(this->addr_state1, &this->state, sizeof(wl_state_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
this->state.pos = this->state.max_pos - 1;
|
||||
}
|
||||
// done. We have recovered the state
|
||||
// If we have a new configuration, we will overwrite it
|
||||
if (this->state.version != this->cfg.version) {
|
||||
result = this->initSections();
|
||||
WL_RESULT_CHECK(result);
|
||||
}
|
||||
}
|
||||
if (result != ESP_OK) {
|
||||
this->initialized = false;
|
||||
ESP_LOGE(TAG, "%s: returned 0x%08x", __func__, (uint32_t)result);
|
||||
return result;
|
||||
}
|
||||
this->initialized = true;
|
||||
ESP_LOGD(TAG, "%s - move_count= 0x%08x", __func__, (uint32_t)this->state.move_count);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t WL_Flash::recoverPos()
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
size_t position = 0;
|
||||
ESP_LOGV(TAG, "%s start", __func__);
|
||||
for (size_t i = 0; i < this->state.max_pos; i++) {
|
||||
bool pos_bits;
|
||||
position = i;
|
||||
result = this->flash_drv->read(this->addr_state1 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
|
||||
pos_bits = this->OkBuffSet(i);
|
||||
WL_RESULT_CHECK(result);
|
||||
ESP_LOGV(TAG, "%s - check pos: result=0x%08x, position= %i, pos_bits= 0x%08x", __func__, (uint32_t)result, (uint32_t)position, (uint32_t)pos_bits);
|
||||
if (pos_bits == false) {
|
||||
break; // we have found position
|
||||
}
|
||||
}
|
||||
|
||||
this->state.pos = position;
|
||||
if (this->state.pos == this->state.max_pos) {
|
||||
this->state.pos--;
|
||||
}
|
||||
ESP_LOGD(TAG, "%s - this->state.pos= 0x%08x, position= 0x%08x, result= 0x%08x, max_pos= 0x%08x", __func__, (uint32_t)this->state.pos, (uint32_t)position, (uint32_t)result, (uint32_t)this->state.max_pos);
|
||||
ESP_LOGV(TAG, "%s done", __func__);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t WL_Flash::initSections()
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
this->state.pos = 0;
|
||||
this->state.access_count = 0;
|
||||
this->state.move_count = 0;
|
||||
// max count
|
||||
this->state.max_count = this->flash_size / this->state_size * this->cfg.updaterate;
|
||||
if (this->cfg.updaterate != 0) {
|
||||
this->state.max_count = this->cfg.updaterate;
|
||||
}
|
||||
this->state.version = this->cfg.version;
|
||||
this->state.block_size = this->cfg.page_size;
|
||||
this->state.device_id = esp_random();
|
||||
memset(this->state.reserved, 0, sizeof(this->state.reserved));
|
||||
|
||||
this->state.max_pos = 1 + this->flash_size / this->cfg.page_size;
|
||||
|
||||
this->state.crc = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, WL_STATE_CRC_LEN_V2);
|
||||
|
||||
result = this->flash_drv->erase_range(this->addr_state1, this->state_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
result = this->flash_drv->write(this->addr_state1, &this->state, sizeof(wl_state_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
// write state copy
|
||||
result = this->flash_drv->erase_range(this->addr_state2, this->state_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
result = this->flash_drv->write(this->addr_state2, &this->state, sizeof(wl_state_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
|
||||
result = this->flash_drv->erase_range(this->addr_cfg, this->cfg_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
result = this->flash_drv->write(this->addr_cfg, &this->cfg, sizeof(wl_config_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
|
||||
ESP_LOGD(TAG, "%s - this->state->max_count= 0x%08x, this->state->max_pos= 0x%08x", __func__, this->state.max_count, this->state.max_pos);
|
||||
ESP_LOGD(TAG, "%s - result= 0x%08x", __func__, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t WL_Flash::updateVersion()
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
|
||||
result = this->updateV1_V2();
|
||||
if (result == ESP_OK) {
|
||||
return result;
|
||||
}
|
||||
// check next version
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t WL_Flash::updateV1_V2()
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
// Check crc for old version and old version
|
||||
ESP_LOGV(TAG, "%s start", __func__);
|
||||
int check_size = WL_STATE_CRC_LEN_V1;
|
||||
// Chech CRC and recover state
|
||||
uint32_t crc1 = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, check_size);
|
||||
wl_state_t sa_copy;
|
||||
wl_state_t *state_copy = &sa_copy;
|
||||
result = this->flash_drv->read(this->addr_state2, state_copy, sizeof(wl_state_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
uint32_t crc2 = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)state_copy, check_size);
|
||||
|
||||
// For V1 crc in place of device_id and version
|
||||
uint32_t v1_crc1 = this->state.device_id;
|
||||
uint32_t v1_crc2 = state_copy->device_id;
|
||||
|
||||
ESP_LOGD(TAG, "%s - process crc1=0x%08x, crc2=0x%08x, v1_crc1=0x%08x, v1_crc2=0x%08x, version=%i", __func__, crc1, crc2, v1_crc1, v1_crc2, this->state.version);
|
||||
|
||||
if ((crc1 == v1_crc1) && (crc2 == v1_crc2) && (v1_crc1 == v1_crc2) && (this->state.version == 1) && (state_copy->version == 1)) {
|
||||
// Here we have to update all internal structures
|
||||
ESP_LOGI(TAG, "%s Update from V1 to V2, crc=0x%08x, ", __func__, crc1);
|
||||
uint32_t pos = 0;
|
||||
|
||||
for (size_t i = 0; i < this->state.max_pos; i++) {
|
||||
uint8_t pos_bits;
|
||||
result = this->flash_drv->read(this->addr_state1 + sizeof(wl_state_t) + i * this->cfg.wr_size, &pos_bits, 1);
|
||||
WL_RESULT_CHECK(result);
|
||||
ESP_LOGV(TAG, "%s- result= 0x%08x, pos= %i, pos_bits= 0x%08x", __func__, (uint32_t)result, (uint32_t)pos, (uint32_t)pos_bits);
|
||||
pos = i;
|
||||
if (pos_bits == 0xff) {
|
||||
break; // we have found position
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG, "%s max_pos=%i, pos=%i, state.ver=%i, state2.ver=%i", __func__, (uint32_t)this->state.max_pos, (uint32_t)pos, (uint32_t)this->state.version, (uint32_t)state_copy->version);
|
||||
if (pos == this->state.max_pos) {
|
||||
pos--;
|
||||
}
|
||||
WL_RESULT_CHECK(result);
|
||||
|
||||
this->state.version = 2;
|
||||
this->state.pos = 0;
|
||||
this->state.device_id = esp_random();
|
||||
memset(this->state.reserved, 0, sizeof(this->state.reserved));
|
||||
this->state.crc = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, WL_STATE_CRC_LEN_V2);
|
||||
|
||||
result = this->flash_drv->erase_range(this->addr_state1, this->state_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
result = this->flash_drv->write(this->addr_state1, &this->state, sizeof(wl_state_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
|
||||
memset(this->temp_buff, 0, this->cfg.wr_size);
|
||||
for (uint32_t i = 0 ; i <= pos; i++) {
|
||||
this->fillOkBuff(i);
|
||||
result = this->flash_drv->write(this->addr_state1 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
}
|
||||
|
||||
result = this->flash_drv->erase_range(this->addr_state2, this->state_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
result = this->flash_drv->write(this->addr_state2, &this->state, sizeof(wl_state_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
ESP_LOGD(TAG, "%s - move_count= 0x%08x, pos= 0x%08x", __func__, this->state.move_count, this->state.pos);
|
||||
|
||||
memset(this->temp_buff, 0, this->cfg.wr_size);
|
||||
for (uint32_t i = 0 ; i <= pos; i++) {
|
||||
this->fillOkBuff(i);
|
||||
result = this->flash_drv->write(this->addr_state2 + sizeof(wl_state_t) + i * this->cfg.wr_size, this->temp_buff, this->cfg.wr_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
}
|
||||
this->state.pos = pos;
|
||||
return result;
|
||||
}
|
||||
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
void WL_Flash::fillOkBuff(int n)
|
||||
{
|
||||
uint32_t *buff = (uint32_t *)this->temp_buff;
|
||||
|
||||
for (int i = 0 ; i < 4 ; i++) {
|
||||
buff[i] = this->state.device_id + n * 4 + i;
|
||||
buff[i] = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&buff[i], sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
|
||||
bool WL_Flash::OkBuffSet(int n)
|
||||
{
|
||||
bool result = true;
|
||||
uint32_t *data_buff = (uint32_t *)this->temp_buff;
|
||||
for (int i = 0 ; i < 4 ; i++) {
|
||||
uint32_t data = this->state.device_id + n * 4 + i;
|
||||
uint32_t crc = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&data, sizeof(uint32_t));
|
||||
if (crc != data_buff[i]) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t WL_Flash::updateWL()
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
this->state.access_count++;
|
||||
if (this->state.access_count < this->state.max_count) {
|
||||
return result;
|
||||
}
|
||||
// Here we have to move the block and increase the state
|
||||
this->state.access_count = 0;
|
||||
ESP_LOGV(TAG, "%s - access_count= 0x%08x, pos= 0x%08x", __func__, this->state.access_count, this->state.pos);
|
||||
// copy data to dummy block
|
||||
size_t data_addr = this->state.pos + 1; // next block, [pos+1] copy to [pos]
|
||||
if (data_addr >= this->state.max_pos) {
|
||||
data_addr = 0;
|
||||
}
|
||||
data_addr = this->cfg.start_addr + data_addr * this->cfg.page_size;
|
||||
this->dummy_addr = this->cfg.start_addr + this->state.pos * this->cfg.page_size;
|
||||
result = this->flash_drv->erase_range(this->dummy_addr, this->cfg.page_size);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s - erase wl dummy sector result= 0x%08x", __func__, result);
|
||||
this->state.access_count = this->state.max_count - 1; // we will update next time
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t copy_count = this->cfg.page_size / this->cfg.temp_buff_size;
|
||||
for (size_t i = 0; i < copy_count; i++) {
|
||||
result = this->flash_drv->read(data_addr + i * this->cfg.temp_buff_size, this->temp_buff, this->cfg.temp_buff_size);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s - not possible to read buffer, will try next time, result= 0x%08x", __func__, result);
|
||||
this->state.access_count = this->state.max_count - 1; // we will update next time
|
||||
return result;
|
||||
}
|
||||
result = this->flash_drv->write(this->dummy_addr + i * this->cfg.temp_buff_size, this->temp_buff, this->cfg.temp_buff_size);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s - not possible to write buffer, will try next time, result= 0x%08x", __func__, result);
|
||||
this->state.access_count = this->state.max_count - 1; // we will update next time
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// done... block moved.
|
||||
// Here we will update structures...
|
||||
// Update bits and save to flash:
|
||||
uint32_t byte_pos = this->state.pos * this->cfg.wr_size;
|
||||
this->fillOkBuff(this->state.pos);
|
||||
// write state to mem. We updating only affected bits
|
||||
result |= this->flash_drv->write(this->addr_state1 + sizeof(wl_state_t) + byte_pos, this->temp_buff, this->cfg.wr_size);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s - update position 1 result= 0x%08x", __func__, result);
|
||||
this->state.access_count = this->state.max_count - 1; // we will update next time
|
||||
return result;
|
||||
}
|
||||
this->fillOkBuff(this->state.pos);
|
||||
result |= this->flash_drv->write(this->addr_state2 + sizeof(wl_state_t) + byte_pos, this->temp_buff, this->cfg.wr_size);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s - update position 2 result= 0x%08x", __func__, result);
|
||||
this->state.access_count = this->state.max_count - 1; // we will update next time
|
||||
return result;
|
||||
}
|
||||
|
||||
this->state.pos++;
|
||||
if (this->state.pos >= this->state.max_pos) {
|
||||
this->state.pos = 0;
|
||||
// one loop more
|
||||
this->state.move_count++;
|
||||
if (this->state.move_count >= (this->state.max_pos - 1)) {
|
||||
this->state.move_count = 0;
|
||||
}
|
||||
// write main state
|
||||
this->state.crc = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, WL_STATE_CRC_LEN_V2);
|
||||
|
||||
result = this->flash_drv->erase_range(this->addr_state1, this->state_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
result = this->flash_drv->write(this->addr_state1, &this->state, sizeof(wl_state_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
result = this->flash_drv->erase_range(this->addr_state2, this->state_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
result = this->flash_drv->write(this->addr_state2, &this->state, sizeof(wl_state_t));
|
||||
WL_RESULT_CHECK(result);
|
||||
ESP_LOGD(TAG, "%s - move_count= 0x%08x, pos= 0x%08x, ", __func__, this->state.move_count, this->state.pos);
|
||||
}
|
||||
// Save structures to the flash... and check result
|
||||
if (result == ESP_OK) {
|
||||
ESP_LOGV(TAG, "%s - result= 0x%08x", __func__, result);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "%s - result= 0x%08x", __func__, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t WL_Flash::calcAddr(size_t addr)
|
||||
{
|
||||
size_t result = (this->flash_size - this->state.move_count * this->cfg.page_size + addr) % this->flash_size;
|
||||
size_t dummy_addr = this->state.pos * this->cfg.page_size;
|
||||
if (result < dummy_addr) {
|
||||
} else {
|
||||
result += this->cfg.page_size;
|
||||
}
|
||||
ESP_LOGV(TAG, "%s - addr= 0x%08x -> result= 0x%08x, dummy_addr= 0x%08x", __func__, (uint32_t) addr, (uint32_t) result, (uint32_t)dummy_addr);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
size_t WL_Flash::chip_size()
|
||||
{
|
||||
if (!this->configured) {
|
||||
return 0;
|
||||
}
|
||||
return this->flash_size;
|
||||
}
|
||||
size_t WL_Flash::sector_size()
|
||||
{
|
||||
if (!this->configured) {
|
||||
return 0;
|
||||
}
|
||||
return this->cfg.sector_size;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t WL_Flash::erase_sector(size_t sector)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
if (!this->initialized) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
ESP_LOGD(TAG, "%s - sector= 0x%08x", __func__, (uint32_t) sector);
|
||||
result = this->updateWL();
|
||||
WL_RESULT_CHECK(result);
|
||||
size_t virt_addr = this->calcAddr(sector * this->cfg.sector_size);
|
||||
result = this->flash_drv->erase_sector((this->cfg.start_addr + virt_addr) / this->cfg.sector_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
return result;
|
||||
}
|
||||
esp_err_t WL_Flash::erase_range(size_t start_address, size_t size)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
if (!this->initialized) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
ESP_LOGD(TAG, "%s - start_address= 0x%08x, size= 0x%08x", __func__, (uint32_t) start_address, (uint32_t) size);
|
||||
size_t erase_count = (size + this->cfg.sector_size - 1) / this->cfg.sector_size;
|
||||
size_t start_sector = start_address / this->cfg.sector_size;
|
||||
for (size_t i = 0; i < erase_count; i++) {
|
||||
result = this->erase_sector(start_sector + i);
|
||||
WL_RESULT_CHECK(result);
|
||||
}
|
||||
ESP_LOGV(TAG, "%s - result= 0x%08x", __func__, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t WL_Flash::write(size_t dest_addr, const void *src, size_t size)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
if (!this->initialized) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
ESP_LOGD(TAG, "%s - dest_addr= 0x%08x, size= 0x%08x", __func__, (uint32_t) dest_addr, (uint32_t) size);
|
||||
uint32_t count = (size - 1) / this->cfg.page_size;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
size_t virt_addr = this->calcAddr(dest_addr + i * this->cfg.page_size);
|
||||
result = this->flash_drv->write(this->cfg.start_addr + virt_addr, &((uint8_t *)src)[i * this->cfg.page_size], this->cfg.page_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
}
|
||||
size_t virt_addr_last = this->calcAddr(dest_addr + count * this->cfg.page_size);
|
||||
result = this->flash_drv->write(this->cfg.start_addr + virt_addr_last, &((uint8_t *)src)[count * this->cfg.page_size], size - count * this->cfg.page_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t WL_Flash::read(size_t src_addr, void *dest, size_t size)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
if (!this->initialized) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
ESP_LOGD(TAG, "%s - src_addr= 0x%08x, size= 0x%08x", __func__, (uint32_t) src_addr, (uint32_t) size);
|
||||
uint32_t count = (size - 1) / this->cfg.page_size;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
size_t virt_addr = this->calcAddr(src_addr + i * this->cfg.page_size);
|
||||
ESP_LOGV(TAG, "%s - real_addr= 0x%08x, size= 0x%08x", __func__, (uint32_t) (this->cfg.start_addr + virt_addr), (uint32_t) size);
|
||||
result = this->flash_drv->read(this->cfg.start_addr + virt_addr, &((uint8_t *)dest)[i * this->cfg.page_size], this->cfg.page_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
}
|
||||
size_t virt_addr_last = this->calcAddr(src_addr + count * this->cfg.page_size);
|
||||
result = this->flash_drv->read(this->cfg.start_addr + virt_addr_last, &((uint8_t *)dest)[count * this->cfg.page_size], size - count * this->cfg.page_size);
|
||||
WL_RESULT_CHECK(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Flash_Access *WL_Flash::get_drv()
|
||||
{
|
||||
return this->flash_drv;
|
||||
}
|
||||
wl_config_t *WL_Flash::get_cfg()
|
||||
{
|
||||
return &this->cfg;
|
||||
}
|
||||
|
||||
esp_err_t WL_Flash::flush()
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
this->state.access_count = this->state.max_count - 1;
|
||||
result = this->updateWL();
|
||||
ESP_LOGD(TAG, "%s - result= 0x%08x, move_count= 0x%08x", __func__, result, this->state.move_count);
|
||||
return result;
|
||||
}
|
1
components/wear_levelling/component.mk
Normal file
1
components/wear_levelling/component.mk
Normal file
@ -0,0 +1 @@
|
||||
COMPONENT_PRIV_INCLUDEDIRS := private_include
|
20
components/wear_levelling/crc32.cpp
Normal file
20
components/wear_levelling/crc32.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "crc32.h"
|
||||
#include "rom/crc.h"
|
||||
|
||||
unsigned int crc32::crc32_le(unsigned int crc, unsigned char const *buf, unsigned int len)
|
||||
{
|
||||
return ::crc32_le(crc, buf, len);
|
||||
}
|
26
components/wear_levelling/crc32.h
Normal file
26
components/wear_levelling/crc32.h
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _crc32_H_
|
||||
#define _crc32_H_
|
||||
|
||||
/**
|
||||
* @brief This class is used to access crc32 module
|
||||
*
|
||||
*/
|
||||
class crc32
|
||||
{
|
||||
public:
|
||||
static unsigned int crc32_le(unsigned int crc, unsigned char const *buf, unsigned int len);
|
||||
};
|
||||
#endif // _crc32_H_
|
93
components/wear_levelling/doc/wl_sw_structure.rst
Normal file
93
components/wear_levelling/doc/wl_sw_structure.rst
Normal file
@ -0,0 +1,93 @@
|
||||
Wear Levelling Component
|
||||
========================
|
||||
|
||||
Wear Levelling Component (WLC) it is a software component that is implemented to prevent situation when some sectors in flash memory used by erase operations more then others. The component shares access attempts between all avalible sectors.
|
||||
The WLC do not have internal cache. When write operation is finished, that means that data was really stored to the flash.
|
||||
As a parameter the WLC requires the driver to access the flash device. The driver has to implement Flash_Access interface.
|
||||
|
||||
The WLC Versioning and Compatibility
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The WLC accept data formats from older version. Latest version of the WLC will update data format from older versions to the current one.
|
||||
Current implementation of WLC has version 2. The data format from current version incompatible with data format from previous versions, and could not be
|
||||
used with previous versions.
|
||||
|
||||
The WLC Files
|
||||
^^^^^^^^^^^^^^^
|
||||
The WLC consist of few components that are implemented in different files. The list and brief description of these components written below.
|
||||
|
||||
- Flash_Access - memory access interface. Used to access the memory. A classes WL_Flash, Partition, SPI_Flash are implements this interface.
|
||||
- SPI_Flash - class implements the Flash_Access interface to provide access to the flash memory.
|
||||
- Partition - class implements the Flash_Access interface to provide access to the partition.
|
||||
- WL_Flash - the main class that implements wear levelling functionality.
|
||||
- WL_State - contains state structure of the WLC.
|
||||
- WL_Config - contains structure to configure the WLC component at startup.
|
||||
- wear_levelling - wrapper API class that provides "C" interface to access the memory through the WLC
|
||||
|
||||
|
||||
Flash_Access Interface
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In the component exist virtual interface Flash_Access. This interface implement main basic functions:
|
||||
- read - read memory to the buffer.
|
||||
- write - writes buffer to the memory.
|
||||
- erase - erase one sector.
|
||||
- erase_range - erase range of memory. The address of rage must be rounded to the sector size.
|
||||
- chip_size - returns the equivalent amount of memory.
|
||||
- sector_size - returns the sector size.
|
||||
- flush - stores current state to the flash, if needed.
|
||||
|
||||
The WLC implements this interface for the user, and requires this interface to access the memory.
|
||||
|
||||
Structure wl_config_t to Configure the WLC at startup
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The wl_config_t contains configuration parameters for the WLC component.
|
||||
- start_addr - offset in the flash memory. The WLC will place all data after this address.
|
||||
- full_mem_size - amount of memory that was allocated and can be used by WLC
|
||||
- sector_size - flash memory sector size
|
||||
- page_size - size of memory for relocation at once. Must be N*sector_size, where N > 0.
|
||||
- updaterate - amount of erase cycles to execute the relocation procedure.
|
||||
- wr_size - smalest possible write access size without erasing of sector.
|
||||
- version - version of the WLC component.
|
||||
- temp_buff_size - amount of memory that the WLC will allocate internally. Must be > 0.
|
||||
|
||||
Internal Memory Organization
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The WLC divide the memory that are define by start_addr and full_mem_size to three regions:
|
||||
- Data
|
||||
- States
|
||||
- Configuration
|
||||
|
||||
The Configuration region used to store configuration information. The user can use it to recover the WLC from memory dump.
|
||||
The Data - is a region where user data stored.
|
||||
The States - is a region where the WLC stores internal information about the WLC state. The States region contains two copies of the WLC states. It is implemented to prevent situation when the device is shut down
|
||||
during operation when the device stores the states. If one of copies is wrong, the WLC can recover the state from another. The broken copy will be overwritten by another.
|
||||
|
||||
Main Idea
|
||||
^^^^^^^^^
|
||||
The WLC has two access addresses: virtual address and real address. The virtual address used by user to access the WLC, the real address used by the WLC to access the real memory.
|
||||
The WLC makes the conversion between virtual and real addresses.
|
||||
The Data region divided to N pages (page could be equal to the sector size). One page defined as dummy page. For user will be available only N-1 pages.
|
||||
The WLC has two internal counters to calculate virtual and real addresses: erase counter and move counter.
|
||||
Every erase operation will be counted by erase counter. When this counter reached the *updaterate* number the page after Dummy page will be moved to the Dummy page, and Dummy page will be changed to this one. The erase counter will
|
||||
be cleared and move counter will be incremented. This procedure will be repeated again and again.
|
||||
When the Dummy page will be at last page in the memory and erase counter will reach the updaterate, the move counter will be cleared and the state will be stored to the State memory.
|
||||
Bellow shown the example with 4 available memory pages. Every state after updaterate erases. The X is a Dummy page.
|
||||
|
||||
- X 0 1 2 - start position
|
||||
- 0 X 1 2 - first move, the page 0 and Dummy page change the places
|
||||
- 0 1 X 2 - second move, the page 1 and Dummy page change the places
|
||||
- 0 1 2 X -
|
||||
- X 1 2 0 - state stored to the memory
|
||||
- 1 X 2 0 -
|
||||
- 1 2 X 0 -
|
||||
- 1 2 0 X -
|
||||
- X 2 0 1 - state stored to the memory
|
||||
- 2 X 0 1 -
|
||||
- 2 0 X 1 -
|
||||
- 2 0 1 X -
|
||||
- X 0 1 2 - state stored to the memory, the memory made full cycle.
|
||||
|
||||
As we see, if user will write data only to one address, amount of erase cycles will be shared between the full memory. The price for that is a one memory page that will not be used by user.
|
||||
|
||||
|
136
components/wear_levelling/include/wear_levelling.h
Normal file
136
components/wear_levelling/include/wear_levelling.h
Normal file
@ -0,0 +1,136 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _wear_levelling_H_
|
||||
#define _wear_levelling_H_
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_partition.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief wear levelling handle
|
||||
*/
|
||||
typedef int32_t wl_handle_t;
|
||||
|
||||
#define WL_INVALID_HANDLE -1
|
||||
|
||||
/**
|
||||
* @brief Mount WL for defined partition
|
||||
*
|
||||
* @param partition that will be used for access
|
||||
* @param out_handle handle of the WL instance
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK, if the allocation was successfully;
|
||||
* - ESP_ERR_INVALID_ARG, if WL allocation was unsuccessful;
|
||||
* - ESP_ERR_NO_MEM, if there was no memory to allocate WL components;
|
||||
*/
|
||||
esp_err_t wl_mount(const esp_partition_t *partition, wl_handle_t *out_handle);
|
||||
|
||||
/**
|
||||
* @brief Unmount WL for defined partition
|
||||
*
|
||||
* @param handle WL partition handle
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK, if the operation completed successfully;
|
||||
* - or one of error codes from lower-level flash driver.
|
||||
*/
|
||||
esp_err_t wl_unmount(wl_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Erase part of the WL storage
|
||||
*
|
||||
* @param handle WL handle that are related to the partition
|
||||
* @param start_addr Address where erase operation should start. Must be aligned
|
||||
* to the result of function wl_sector_size(...).
|
||||
* @param size Size of the range which should be erased, in bytes.
|
||||
* Must be divisible by result of function wl_sector_size(...)..
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK, if the range was erased successfully;
|
||||
* - ESP_ERR_INVALID_ARG, if iterator or dst are NULL;
|
||||
* - ESP_ERR_INVALID_SIZE, if erase would go out of bounds of the partition;
|
||||
* - or one of error codes from lower-level flash driver.
|
||||
*/
|
||||
esp_err_t wl_erase_range(wl_handle_t handle, size_t start_addr, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Write data to the WL storage
|
||||
*
|
||||
* Before writing data to flash, corresponding region of flash needs to be erased.
|
||||
* This can be done using wl_erase_range function.
|
||||
*
|
||||
* @param handle WL handle that are related to the partition
|
||||
* @param dest_addr Address where the data should be written, relative to the
|
||||
* beginning of the partition.
|
||||
* @param src Pointer to the source buffer. Pointer must be non-NULL and
|
||||
* buffer must be at least 'size' bytes long.
|
||||
* @param size Size of data to be written, in bytes.
|
||||
*
|
||||
* @note Prior to writing to WL storage, make sure it has been erased with
|
||||
* wl_erase_range call.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK, if data was written successfully;
|
||||
* - ESP_ERR_INVALID_ARG, if dst_offset exceeds partition size;
|
||||
* - ESP_ERR_INVALID_SIZE, if write would go out of bounds of the partition;
|
||||
* - or one of error codes from lower-level flash driver.
|
||||
*/
|
||||
esp_err_t wl_write(wl_handle_t handle, size_t dest_addr, const void *src, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Read data from the WL storage
|
||||
*
|
||||
* @param handle WL module instance that was initialized before
|
||||
* @param dest Pointer to the buffer where data should be stored.
|
||||
* Pointer must be non-NULL and buffer must be at least 'size' bytes long.
|
||||
* @param src_addr Address of the data to be read, relative to the
|
||||
* beginning of the partition.
|
||||
* @param size Size of data to be read, in bytes.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK, if data was read successfully;
|
||||
* - ESP_ERR_INVALID_ARG, if src_offset exceeds partition size;
|
||||
* - ESP_ERR_INVALID_SIZE, if read would go out of bounds of the partition;
|
||||
* - or one of error codes from lower-level flash driver.
|
||||
*/
|
||||
esp_err_t wl_read(wl_handle_t handle, size_t src_addr, void *dest, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Get size of the WL storage
|
||||
*
|
||||
* @param handle WL module handle that was initialized before
|
||||
* @return usable size, in bytes
|
||||
*/
|
||||
size_t wl_size(wl_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Get sector size of the WL instance
|
||||
*
|
||||
* @param handle WL module handle that was initialized before
|
||||
* @return sector size, in bytes
|
||||
*/
|
||||
size_t wl_sector_size(wl_handle_t handle);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _wear_levelling_H_
|
44
components/wear_levelling/private_include/Flash_Access.h
Normal file
44
components/wear_levelling/private_include/Flash_Access.h
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
#ifndef _Flash_Access_H_
|
||||
#define _Flash_Access_H_
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* @brief Universal flash access interface class
|
||||
*
|
||||
*/
|
||||
class Flash_Access
|
||||
{
|
||||
public:
|
||||
virtual size_t chip_size() = 0;
|
||||
|
||||
virtual esp_err_t erase_sector(size_t sector) = 0;
|
||||
virtual esp_err_t erase_range(size_t start_address, size_t size) = 0;
|
||||
|
||||
virtual esp_err_t write(size_t dest_addr, const void *src, size_t size) = 0;
|
||||
virtual esp_err_t read(size_t src_addr, void *dest, size_t size) = 0;
|
||||
|
||||
virtual size_t sector_size() = 0;
|
||||
|
||||
virtual esp_err_t flush()
|
||||
{
|
||||
return ESP_OK;
|
||||
};
|
||||
|
||||
virtual ~Flash_Access() {};
|
||||
};
|
||||
|
||||
#endif // _Flash_Access_H_
|
36
components/wear_levelling/private_include/Partition.h
Normal file
36
components/wear_levelling/private_include/Partition.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef _Partition_H_
|
||||
#define _Partition_H_
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#include "Flash_Access.h"
|
||||
#include "esp_partition.h"
|
||||
|
||||
/**
|
||||
* @brief This class is used to access partition. Class implements Flash_Access interface
|
||||
*
|
||||
*/
|
||||
class Partition : public Flash_Access
|
||||
{
|
||||
|
||||
public:
|
||||
Partition(const esp_partition_t *partition);
|
||||
|
||||
virtual size_t chip_size();
|
||||
|
||||
virtual esp_err_t erase_sector(size_t sector);
|
||||
virtual esp_err_t erase_range(size_t start_address, size_t size);
|
||||
|
||||
virtual esp_err_t write(size_t dest_addr, const void *src, size_t size);
|
||||
virtual esp_err_t read(size_t src_addr, void *dest, size_t size);
|
||||
|
||||
virtual size_t sector_size();
|
||||
|
||||
virtual ~Partition();
|
||||
protected:
|
||||
const esp_partition_t *partition;
|
||||
|
||||
};
|
||||
|
||||
#endif // _Partition_H_
|
||||
|
26
components/wear_levelling/private_include/SPI_Flash.h
Normal file
26
components/wear_levelling/private_include/SPI_Flash.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef _SPI_Flash_H_
|
||||
#define _SPI_Flash_H_
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "Flash_Access.h"
|
||||
|
||||
/**
|
||||
* @brief This class is used to access SPI flash devices. Class implements Flash_Access interface
|
||||
*
|
||||
*/
|
||||
class SPI_Flash : public Flash_Access
|
||||
{
|
||||
|
||||
public:
|
||||
SPI_Flash();
|
||||
|
||||
size_t chip_size() override;
|
||||
esp_err_t erase_sector(size_t sector) override;
|
||||
esp_err_t erase_range(size_t start_address, size_t size) override;
|
||||
esp_err_t write(size_t dest_addr, const void *src, size_t size) override;
|
||||
esp_err_t read(size_t src_addr, void *dest, size_t size) override;
|
||||
size_t sector_size() override;
|
||||
~SPI_Flash() override;
|
||||
};
|
||||
|
||||
#endif // _SPI_Flash_H_
|
48
components/wear_levelling/private_include/WL_Config.h
Normal file
48
components/wear_levelling/private_include/WL_Config.h
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _WL_Config_H_
|
||||
#define _WL_Config_H_
|
||||
|
||||
#include "Flash_Access.h"
|
||||
|
||||
/**
|
||||
* @brief This class is used as a structure to configure wear levelling module
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define ALIGNED_(x) __declspec(align(x))
|
||||
#else
|
||||
#if defined(__GNUC__)
|
||||
#define ALIGNED_(x) __attribute__ ((aligned(x)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct ALIGNED_(16) WL_Config_s { /*!< Size of wl_config_t structure should be divided by 16 for encryption*/
|
||||
size_t start_addr; /*!< start address in the flash*/
|
||||
uint32_t full_mem_size; /*!< Amount of memory used to store data in bytes*/
|
||||
uint32_t page_size; /*!< One page size in bytes. Page could be more then memory block. This parameter must be page_size >= N*block_size.*/
|
||||
uint32_t sector_size; /*!< size of flash memory sector that will be erased and stored at once (erase)*/
|
||||
uint32_t updaterate; /*!< Amount of accesses before block will be moved*/
|
||||
uint32_t wr_size; /*!< Minimum amount of bytes per one block at write operation: 1...*/
|
||||
uint32_t version; /*!< A version of current implementatioon. To erase and reallocate complete memory this ID must be different from id before.*/
|
||||
size_t temp_buff_size; /*!< Size of temporary allocated buffer to copy from one flash area to another. The best way, if this value will be equal to sector size.*/
|
||||
uint32_t crc; /*!< CRC for this config*/
|
||||
} wl_config_t;
|
||||
|
||||
#ifndef _MSC_VER // MSVS has different format for this define
|
||||
static_assert(sizeof(wl_config_t) % 16 == 0, "Size of wl_config_t structure should be compatible with flash encryption");
|
||||
#endif // _MSC_VER
|
||||
|
||||
#endif // _WL_Config_H_
|
22
components/wear_levelling/private_include/WL_Ext_Cfg.h
Normal file
22
components/wear_levelling/private_include/WL_Ext_Cfg.h
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _WL_Ext_Cfg_H_
|
||||
#define _WL_Ext_Cfg_H_
|
||||
#include "WL_Config.h"
|
||||
|
||||
typedef struct WL_Ext_Cfg_s : public WL_Config_s {
|
||||
uint32_t fat_sector_size; /*!< virtual sector size*/
|
||||
} wl_ext_cfg_t;
|
||||
|
||||
#endif // _WL_Ext_Cfg_H_
|
46
components/wear_levelling/private_include/WL_Ext_Perf.h
Normal file
46
components/wear_levelling/private_include/WL_Ext_Perf.h
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _WL_Ext_Perf_H_
|
||||
#define _WL_Ext_Perf_H_
|
||||
|
||||
#include "WL_Flash.h"
|
||||
#include "WL_Ext_Cfg.h"
|
||||
|
||||
class WL_Ext_Perf : public WL_Flash
|
||||
{
|
||||
public:
|
||||
WL_Ext_Perf();
|
||||
~WL_Ext_Perf() override;
|
||||
|
||||
esp_err_t config(WL_Config_s *cfg, Flash_Access *flash_drv) override;
|
||||
esp_err_t init() override;
|
||||
|
||||
size_t chip_size() override;
|
||||
size_t sector_size() override;
|
||||
|
||||
|
||||
esp_err_t erase_sector(size_t sector) override;
|
||||
esp_err_t erase_range(size_t start_address, size_t size) override;
|
||||
|
||||
protected:
|
||||
uint32_t flash_sector_size;
|
||||
uint32_t fat_sector_size;
|
||||
uint32_t size_factor;
|
||||
uint32_t *sector_buffer;
|
||||
|
||||
virtual esp_err_t erase_sector_fit(uint32_t start_sector, uint32_t count);
|
||||
|
||||
};
|
||||
|
||||
#endif // _WL_Ext_Perf_H_
|
42
components/wear_levelling/private_include/WL_Ext_Safe.h
Normal file
42
components/wear_levelling/private_include/WL_Ext_Safe.h
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _WL_Ext_Safe_H_
|
||||
#define _WL_Ext_Safe_H_
|
||||
|
||||
#include "WL_Flash.h"
|
||||
#include "WL_Ext_Cfg.h"
|
||||
#include "WL_Ext_Perf.h"
|
||||
|
||||
class WL_Ext_Safe : public WL_Ext_Perf
|
||||
{
|
||||
public:
|
||||
WL_Ext_Safe();
|
||||
~WL_Ext_Safe() override;
|
||||
|
||||
esp_err_t config(WL_Config_s *cfg, Flash_Access *flash_drv) override;
|
||||
esp_err_t init() override;
|
||||
|
||||
size_t chip_size() override;
|
||||
|
||||
protected:
|
||||
esp_err_t erase_sector_fit(uint32_t start_sector, uint32_t count) override;
|
||||
|
||||
// Dump Sector
|
||||
uint32_t dump_addr; // dump buffer address
|
||||
uint32_t state_addr;// sectore where state of transaction will be stored
|
||||
|
||||
esp_err_t recover();
|
||||
};
|
||||
|
||||
#endif // _WL_Ext_Safe_H_
|
81
components/wear_levelling/private_include/WL_Flash.h
Normal file
81
components/wear_levelling/private_include/WL_Flash.h
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _WL_Flash_H_
|
||||
#define _WL_Flash_H_
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "Flash_Access.h"
|
||||
#include "WL_Config.h"
|
||||
#include "WL_State.h"
|
||||
|
||||
/**
|
||||
* @brief This class is used to make wear levelling for flash devices. Class implements Flash_Access interface
|
||||
*
|
||||
*/
|
||||
class WL_Flash : public Flash_Access
|
||||
{
|
||||
public :
|
||||
WL_Flash();
|
||||
~WL_Flash() override;
|
||||
|
||||
virtual esp_err_t config(wl_config_t *cfg, Flash_Access *flash_drv);
|
||||
virtual esp_err_t init();
|
||||
|
||||
size_t chip_size() override;
|
||||
size_t sector_size() override;
|
||||
|
||||
|
||||
esp_err_t erase_sector(size_t sector) override;
|
||||
esp_err_t erase_range(size_t start_address, size_t size) override;
|
||||
|
||||
esp_err_t write(size_t dest_addr, const void *src, size_t size) override;
|
||||
esp_err_t read(size_t src_addr, void *dest, size_t size) override;
|
||||
|
||||
esp_err_t flush() override;
|
||||
|
||||
Flash_Access *get_drv();
|
||||
wl_config_t *get_cfg();
|
||||
|
||||
protected:
|
||||
bool configured = false;
|
||||
bool initialized = false;
|
||||
wl_state_t state;
|
||||
wl_config_t cfg;
|
||||
Flash_Access *flash_drv = NULL;
|
||||
|
||||
size_t addr_cfg;
|
||||
size_t addr_state1;
|
||||
size_t addr_state2;
|
||||
size_t index_state1;
|
||||
size_t index_state2;
|
||||
|
||||
size_t flash_size;
|
||||
uint32_t state_size;
|
||||
uint32_t cfg_size;
|
||||
uint8_t *temp_buff = NULL;
|
||||
size_t dummy_addr;
|
||||
uint32_t pos_data[4];
|
||||
|
||||
esp_err_t initSections();
|
||||
esp_err_t updateWL();
|
||||
esp_err_t recoverPos();
|
||||
size_t calcAddr(size_t addr);
|
||||
|
||||
esp_err_t updateVersion();
|
||||
esp_err_t updateV1_V2();
|
||||
void fillOkBuff(int n);
|
||||
bool OkBuffSet(int n);
|
||||
};
|
||||
|
||||
#endif // _WL_Flash_H_
|
51
components/wear_levelling/private_include/WL_State.h
Normal file
51
components/wear_levelling/private_include/WL_State.h
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _WL_State_H_
|
||||
#define _WL_State_H_
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* @brief This structure is used to store current state of flash access
|
||||
*
|
||||
*/
|
||||
#if defined(_MSC_VER)
|
||||
#define ALIGNED_(x) __declspec(align(x))
|
||||
#else
|
||||
#if defined(__GNUC__)
|
||||
#define ALIGNED_(x) __attribute__ ((aligned(x)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct ALIGNED_(32) WL_State_s {
|
||||
public:
|
||||
uint32_t pos; /*!< current dummy block position*/
|
||||
uint32_t max_pos; /*!< maximum amount of positions*/
|
||||
uint32_t move_count; /*!< total amount of move counts. Used to calculate the address*/
|
||||
uint32_t access_count; /*!< current access count*/
|
||||
uint32_t max_count; /*!< max access count when block will be moved*/
|
||||
uint32_t block_size; /*!< size of move block*/
|
||||
uint32_t version; /*!< state id used to identify the version of current libary implementaion*/
|
||||
uint32_t device_id; /*!< ID of current WL instance*/
|
||||
uint32_t reserved[7]; /*!< Reserved space for future use*/
|
||||
uint32_t crc; /*!< CRC of structure*/
|
||||
} wl_state_t;
|
||||
|
||||
#ifndef _MSC_VER // MSVS has different format for this define
|
||||
static_assert(sizeof(wl_state_t) % 16 == 0, "Size of wl_state_t structure should be compatible with flash encryption");
|
||||
#endif // _MSC_VER
|
||||
|
||||
#define WL_STATE_CRC_LEN_V1 offsetof(wl_state_t, device_id)
|
||||
#define WL_STATE_CRC_LEN_V2 offsetof(wl_state_t, crc)
|
||||
|
||||
#endif // _WL_State_H_
|
5
components/wear_levelling/test/CMakeLists.txt
Normal file
5
components/wear_levelling/test/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES unity test_utils wear_levelling
|
||||
EMBED_FILES test_partition_v1.bin
|
||||
)
|
2
components/wear_levelling/test/component.mk
Normal file
2
components/wear_levelling/test/component.mk
Normal file
@ -0,0 +1,2 @@
|
||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
||||
COMPONENT_EMBED_FILES := test_partition_v1.bin
|
BIN
components/wear_levelling/test/test_partition_v1.bin
Normal file
BIN
components/wear_levelling/test/test_partition_v1.bin
Normal file
Binary file not shown.
320
components/wear_levelling/test/test_wl.c
Normal file
320
components/wear_levelling/test/test_wl.c
Normal file
@ -0,0 +1,320 @@
|
||||
#include "sdkconfig.h"
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "wear_levelling.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_timer.h"
|
||||
#include "test_utils.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
//#include "freertos/portable.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
#include "esp32/clk.h"
|
||||
#include "soc/cpu.h"
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2BETA)
|
||||
#include "esp32s2beta/clk.h"
|
||||
#include "soc/cpu.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP8266
|
||||
#include "esp_clk.h"
|
||||
#endif
|
||||
|
||||
#define xPortGetFreeHeapSize() esp_get_free_heap_size()
|
||||
#define RSR(_c, _t) _t = (uint32_t) esp_timer_get_time()
|
||||
|
||||
TEST_CASE("wl_unmount doesn't leak memory", "[wear_levelling]")
|
||||
{
|
||||
const esp_partition_t *partition = get_test_data_partition();
|
||||
wl_handle_t handle;
|
||||
// dummy unmount is needed to initialize static lock in WL
|
||||
wl_unmount(WL_INVALID_HANDLE);
|
||||
size_t size_before = xPortGetFreeHeapSize();
|
||||
TEST_ESP_OK(wl_mount(partition, &handle));
|
||||
wl_unmount(handle);
|
||||
size_t size_after = xPortGetFreeHeapSize();
|
||||
|
||||
// Original code:
|
||||
//TEST_ASSERT_EQUAL_HEX32(size_before, size_after);
|
||||
// Workaround for problem with heap size calculation:
|
||||
ptrdiff_t stack_diff = size_before - size_after;
|
||||
stack_diff = abs(stack_diff);
|
||||
if (stack_diff > 8) TEST_ASSERT_EQUAL(0, stack_diff);
|
||||
}
|
||||
|
||||
TEST_CASE("wl_mount check partition parameters", "[wear_levelling][ignore]")
|
||||
{
|
||||
const esp_partition_t *test_partition = get_test_data_partition();
|
||||
esp_partition_t fake_partition;
|
||||
memcpy(&fake_partition, test_partition, sizeof(fake_partition));
|
||||
wl_handle_t handle;
|
||||
size_t size_before, size_after;
|
||||
wl_unmount(WL_INVALID_HANDLE);
|
||||
|
||||
esp_partition_erase_range(test_partition, 0, test_partition->size);
|
||||
// test small partition: result should be error
|
||||
for (int i=0 ; i< 5 ; i++)
|
||||
{
|
||||
fake_partition.size = SPI_FLASH_SEC_SIZE*(i);
|
||||
size_before = xPortGetFreeHeapSize();
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, wl_mount(&fake_partition, &handle));
|
||||
size_after = xPortGetFreeHeapSize();
|
||||
|
||||
// Original code:
|
||||
//TEST_ASSERT_EQUAL_HEX32(size_before, size_after);
|
||||
// Workaround for problem with heap size calculation:
|
||||
ptrdiff_t stack_diff = size_before - size_after;
|
||||
stack_diff = abs(stack_diff);
|
||||
if (stack_diff > 8) TEST_ASSERT_EQUAL(0, stack_diff);
|
||||
}
|
||||
|
||||
// test minimum size partition: result should be OK
|
||||
fake_partition.size = SPI_FLASH_SEC_SIZE * 5;
|
||||
size_before = xPortGetFreeHeapSize();
|
||||
TEST_ESP_OK(wl_mount(&fake_partition, &handle));
|
||||
wl_unmount(handle);
|
||||
printf("Test done\n");
|
||||
size_after = xPortGetFreeHeapSize();
|
||||
|
||||
// Original code:
|
||||
//TEST_ASSERT_EQUAL_HEX32(size_before, size_after);
|
||||
// Workaround for problem with heap size calculation:
|
||||
ptrdiff_t stack_diff = size_before - size_after;
|
||||
stack_diff = abs(stack_diff);
|
||||
if (stack_diff > 8) TEST_ASSERT_EQUAL(0, stack_diff);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
size_t offset;
|
||||
bool write;
|
||||
size_t word_count;
|
||||
int seed;
|
||||
SemaphoreHandle_t done;
|
||||
int result;
|
||||
wl_handle_t handle;
|
||||
} read_write_test_arg_t;
|
||||
|
||||
#define READ_WRITE_TEST_ARG_INIT(offset_, seed_, handle_, count_) \
|
||||
{ \
|
||||
.offset = offset_, \
|
||||
.seed = seed_, \
|
||||
.word_count = count_, \
|
||||
.write = true, \
|
||||
.done = xSemaphoreCreateBinary(), \
|
||||
.handle = handle_ \
|
||||
}
|
||||
|
||||
static void read_write_task(void* param)
|
||||
{
|
||||
read_write_test_arg_t* args = (read_write_test_arg_t*) param;
|
||||
esp_err_t err;
|
||||
srand(args->seed);
|
||||
for (size_t i = 0; i < args->word_count; ++i) {
|
||||
uint32_t val = i * 77;
|
||||
if (args->write) {
|
||||
err = wl_write(args->handle, args->offset + i * sizeof(val), &val, sizeof(val));
|
||||
if (err != ESP_OK) {
|
||||
args->result = err;
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
uint32_t rval;
|
||||
err = wl_read(args->handle, args->offset + i * sizeof(rval), &rval, sizeof(rval));
|
||||
if (err != ESP_OK || rval != val) {
|
||||
ets_printf("E: i=%d, cnt=%d rval=%d val=%d\n\n", i, args->word_count, rval, val);
|
||||
args->result = ESP_FAIL;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
args->result = ESP_OK;
|
||||
|
||||
done:
|
||||
xSemaphoreGive(args->done);
|
||||
vTaskDelay(1);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("multiple tasks can access wl handle simultaneously", "[wear_levelling]")
|
||||
{
|
||||
const esp_partition_t *partition = get_test_data_partition();
|
||||
wl_handle_t handle;
|
||||
TEST_ESP_OK(wl_mount(partition, &handle));
|
||||
|
||||
size_t sector_size = wl_sector_size(handle);
|
||||
TEST_ESP_OK(wl_erase_range(handle, 0, sector_size * 8));
|
||||
read_write_test_arg_t args1 = READ_WRITE_TEST_ARG_INIT(0, 1, handle, sector_size/sizeof(uint32_t));
|
||||
read_write_test_arg_t args2 = READ_WRITE_TEST_ARG_INIT(sector_size, 2, handle, sector_size/sizeof(uint32_t));
|
||||
const size_t stack_size = 4096;
|
||||
|
||||
printf("writing 1 and 2\n");
|
||||
const int cpuid_0 = 0;
|
||||
const int cpuid_1 = portNUM_PROCESSORS - 1;
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw1", stack_size, &args1, 3, NULL, cpuid_0);
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw2", stack_size, &args2, 3, NULL, cpuid_1);
|
||||
|
||||
xSemaphoreTake(args1.done, portMAX_DELAY);
|
||||
printf("f1 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args1.result);
|
||||
xSemaphoreTake(args2.done, portMAX_DELAY);
|
||||
printf("f2 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args2.result);
|
||||
|
||||
args1.write = false;
|
||||
args2.write = false;
|
||||
read_write_test_arg_t args3 = READ_WRITE_TEST_ARG_INIT(2 * sector_size, 3, handle, sector_size/sizeof(uint32_t));
|
||||
read_write_test_arg_t args4 = READ_WRITE_TEST_ARG_INIT(3 * sector_size, 4, handle, sector_size/sizeof(uint32_t));
|
||||
|
||||
printf("reading 1 and 2, writing 3 and 4\n");
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw3", stack_size, &args3, 3, NULL, cpuid_1);
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw4", stack_size, &args4, 3, NULL, cpuid_0);
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw1", stack_size, &args1, 3, NULL, cpuid_0);
|
||||
xTaskCreatePinnedToCore(&read_write_task, "rw2", stack_size, &args2, 3, NULL, cpuid_1);
|
||||
|
||||
xSemaphoreTake(args1.done, portMAX_DELAY);
|
||||
printf("f1 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args1.result);
|
||||
xSemaphoreTake(args2.done, portMAX_DELAY);
|
||||
printf("f2 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args2.result);
|
||||
xSemaphoreTake(args3.done, portMAX_DELAY);
|
||||
printf("f3 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args3.result);
|
||||
xSemaphoreTake(args4.done, portMAX_DELAY);
|
||||
printf("f4 done\n");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, args4.result);
|
||||
|
||||
vSemaphoreDelete(args1.done);
|
||||
vSemaphoreDelete(args2.done);
|
||||
vSemaphoreDelete(args3.done);
|
||||
vSemaphoreDelete(args4.done);
|
||||
wl_unmount(handle);
|
||||
}
|
||||
|
||||
#define TEST_SECTORS_COUNT 8
|
||||
|
||||
static void check_mem_data(wl_handle_t handle, uint32_t init_val, uint32_t* buff)
|
||||
{
|
||||
size_t sector_size = wl_sector_size(handle);
|
||||
|
||||
for (int m=0 ; m < TEST_SECTORS_COUNT ; m++) {
|
||||
TEST_ESP_OK(wl_read(handle, sector_size * m, buff, sector_size));
|
||||
for (int i=0 ; i< sector_size/sizeof(uint32_t) ; i++) {
|
||||
uint32_t compare_val = init_val + i + m*sector_size;
|
||||
TEST_ASSERT_EQUAL( buff[i], compare_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We write complete memory with defined data
|
||||
// And then write one sector many times.
|
||||
// A data in other secors should be the same.
|
||||
// We do this also with unmount
|
||||
TEST_CASE("multiple write is correct", "[wear_levelling]")
|
||||
{
|
||||
const esp_partition_t *partition = get_test_data_partition();
|
||||
esp_partition_t fake_partition;
|
||||
memcpy(&fake_partition, partition, sizeof(fake_partition));
|
||||
|
||||
fake_partition.size = SPI_FLASH_SEC_SIZE*(4 + TEST_SECTORS_COUNT);
|
||||
|
||||
wl_handle_t handle;
|
||||
TEST_ESP_OK(wl_mount(&fake_partition, &handle));
|
||||
|
||||
size_t sector_size = wl_sector_size(handle);
|
||||
// Erase 8 sectors
|
||||
TEST_ESP_OK(wl_erase_range(handle, 0, sector_size * TEST_SECTORS_COUNT));
|
||||
// Write data to all sectors
|
||||
printf("Check 1 sector_size=0x%08x\n", sector_size);
|
||||
// Set initial random value
|
||||
uint32_t init_val = rand();
|
||||
|
||||
uint32_t* buff = (uint32_t*)malloc(sector_size);
|
||||
for (int m=0 ; m < TEST_SECTORS_COUNT ; m++) {
|
||||
for (int i=0 ; i< sector_size/sizeof(uint32_t) ; i++) {
|
||||
buff[i] = init_val + i + m*sector_size;
|
||||
}
|
||||
TEST_ESP_OK(wl_erase_range(handle, sector_size*m, sector_size));
|
||||
TEST_ESP_OK(wl_write(handle, sector_size*m, buff, sector_size));
|
||||
}
|
||||
|
||||
check_mem_data(handle, init_val, buff);
|
||||
|
||||
uint32_t start;
|
||||
RSR(CCOUNT, start);
|
||||
|
||||
|
||||
for (int m=0 ; m< 100000 ; m++) {
|
||||
uint32_t sector = m % TEST_SECTORS_COUNT;
|
||||
for (int i=0 ; i< sector_size/sizeof(uint32_t) ; i++) {
|
||||
buff[i] = init_val + i + sector*sector_size;
|
||||
}
|
||||
TEST_ESP_OK(wl_erase_range(handle, sector_size*sector, sector_size));
|
||||
TEST_ESP_OK(wl_write(handle, sector_size*sector, buff, sector_size));
|
||||
check_mem_data(handle, init_val, buff);
|
||||
|
||||
uint32_t end;
|
||||
RSR(CCOUNT, end);
|
||||
uint32_t ms = (end - start) / (esp_clk_cpu_freq() / 1000);
|
||||
printf("loop %4i pass, time= %ims\n", m, ms);
|
||||
if (ms > 10000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(buff);
|
||||
wl_unmount(handle);
|
||||
}
|
||||
|
||||
extern const uint8_t test_partition_v1_bin_start[] asm("_binary_test_partition_v1_bin_start");
|
||||
extern const uint8_t test_partition_v1_bin_end[] asm("_binary_test_partition_v1_bin_end");
|
||||
|
||||
#define COMPARE_START_CONST 0x12340000
|
||||
|
||||
// We write to partition prepared image with V1
|
||||
// Then we convert image to new version and verifying the data
|
||||
|
||||
TEST_CASE("Version update test", "[wear_levelling]")
|
||||
{
|
||||
const esp_partition_t *partition = get_test_data_partition();
|
||||
esp_partition_t fake_partition;
|
||||
memcpy(&fake_partition, partition, sizeof(fake_partition));
|
||||
|
||||
if (partition->encrypted)
|
||||
{
|
||||
printf("Update from V1 to V2 will not work.\n");
|
||||
return;
|
||||
}
|
||||
fake_partition.size = (size_t)(test_partition_v1_bin_end - test_partition_v1_bin_start);
|
||||
|
||||
printf("Data file size = %i, partition address = 0x%08x, file addr=0x%08x\n", (uint32_t)fake_partition.size, (uint32_t)fake_partition.address, (uint32_t)test_partition_v1_bin_start);
|
||||
|
||||
esp_partition_erase_range(&fake_partition, 0, fake_partition.size);
|
||||
|
||||
esp_partition_write(&fake_partition, 0, test_partition_v1_bin_start, fake_partition.size);
|
||||
for (int i=0 ; i< 3 ; i++)
|
||||
{
|
||||
printf("Pass %i\n", i);
|
||||
wl_handle_t handle;
|
||||
TEST_ESP_OK(wl_mount(&fake_partition, &handle));
|
||||
size_t sector_size = wl_sector_size(handle);
|
||||
uint32_t* buff = (uint32_t*)malloc(sector_size);
|
||||
|
||||
uint32_t init_val = COMPARE_START_CONST;
|
||||
int test_count = fake_partition.size/sector_size - 4;
|
||||
|
||||
for (int m=0 ; m < test_count; m++) {
|
||||
TEST_ESP_OK(wl_read(handle, sector_size * m, buff, sector_size));
|
||||
for (int i=0 ; i< sector_size/sizeof(uint32_t) ; i++) {
|
||||
uint32_t compare_val = init_val + i + m*sector_size;
|
||||
if (buff[i] != compare_val)
|
||||
{
|
||||
printf("error compare: 0x%08x != 0x%08x \n", buff[i], compare_val);
|
||||
}
|
||||
TEST_ASSERT_EQUAL( buff[i], compare_val);
|
||||
}
|
||||
}
|
||||
free(buff);
|
||||
wl_unmount(handle);
|
||||
}
|
||||
}
|
98
components/wear_levelling/test_wl_host/Makefile
Normal file
98
components/wear_levelling/test_wl_host/Makefile
Normal file
@ -0,0 +1,98 @@
|
||||
ifndef COMPONENT
|
||||
COMPONENT := wl
|
||||
endif
|
||||
|
||||
COMPONENT_LIB := lib$(COMPONENT).a
|
||||
TEST_PROGRAM := test_$(COMPONENT)
|
||||
|
||||
STUBS_LIB_DIR := ../../../components/spi_flash/sim/stubs
|
||||
STUBS_LIB_BUILD_DIR := $(STUBS_LIB_DIR)/build
|
||||
STUBS_LIB := libstubs.a
|
||||
|
||||
SPI_FLASH_SIM_DIR := ../../../components/spi_flash/sim
|
||||
SPI_FLASH_SIM_BUILD_DIR := $(SPI_FLASH_SIM_DIR)/build
|
||||
SPI_FLASH_SIM_LIB := libspi_flash.a
|
||||
|
||||
include Makefile.files
|
||||
|
||||
all: test
|
||||
|
||||
|
||||
ifndef SDKCONFIG
|
||||
SDKCONFIG_DIR := $(dir $(realpath sdkconfig/sdkconfig.h))
|
||||
SDKCONFIG := $(SDKCONFIG_DIR)sdkconfig.h
|
||||
else
|
||||
SDKCONFIG_DIR := $(dir $(realpath $(SDKCONFIG)))
|
||||
endif
|
||||
|
||||
INCLUDE_FLAGS := $(addprefix -I, $(INCLUDE_DIRS) $(SDKCONFIG_DIR) ../../../tools/catch)
|
||||
|
||||
CPPFLAGS += $(INCLUDE_FLAGS) -g -m32
|
||||
CXXFLAGS += $(INCLUDE_FLAGS) -std=c++11 -g -m32
|
||||
|
||||
# Build libraries that this component is dependent on
|
||||
$(STUBS_LIB_BUILD_DIR)/$(STUBS_LIB): force
|
||||
$(MAKE) -C $(STUBS_LIB_DIR) lib SDKCONFIG=$(SDKCONFIG)
|
||||
|
||||
$(SPI_FLASH_SIM_BUILD_DIR)/$(SPI_FLASH_SIM_LIB): force
|
||||
$(MAKE) -C $(SPI_FLASH_SIM_DIR) lib SDKCONFIG=$(SDKCONFIG)
|
||||
|
||||
# Create target for building this component as a library
|
||||
CFILES := $(filter %.c, $(SOURCE_FILES))
|
||||
CPPFILES := $(filter %.cpp, $(SOURCE_FILES))
|
||||
|
||||
CTARGET = ${2}/$(patsubst %.c,%.o,$(notdir ${1}))
|
||||
CPPTARGET = ${2}/$(patsubst %.cpp,%.o,$(notdir ${1}))
|
||||
|
||||
ifndef BUILD_DIR
|
||||
BUILD_DIR := build
|
||||
endif
|
||||
|
||||
OBJ_FILES := $(addprefix $(BUILD_DIR)/, $(filter %.o, $(notdir $(SOURCE_FILES:.cpp=.o) $(SOURCE_FILES:.c=.o))))
|
||||
|
||||
define COMPILE_C
|
||||
$(call CTARGET, ${1}, $(BUILD_DIR)) : ${1} $(SDKCONFIG)
|
||||
mkdir -p $(BUILD_DIR)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(call CTARGET, ${1}, $(BUILD_DIR)) ${1}
|
||||
endef
|
||||
|
||||
define COMPILE_CPP
|
||||
$(call CPPTARGET, ${1}, $(BUILD_DIR)) : ${1} $(SDKCONFIG)
|
||||
mkdir -p $(BUILD_DIR)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $(call CPPTARGET, ${1}, $(BUILD_DIR)) ${1}
|
||||
endef
|
||||
|
||||
$(BUILD_DIR)/$(COMPONENT_LIB): $(OBJ_FILES) $(SDKCONFIG)
|
||||
mkdir -p $(BUILD_DIR)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(STUBS_LIB_DIR) clean
|
||||
$(MAKE) -C $(SPI_FLASH_SIM_DIR) clean
|
||||
rm -f $(OBJ_FILES) $(TEST_OBJ_FILES) $(TEST_PROGRAM) $(COMPONENT_LIB) partition_table.bin
|
||||
|
||||
lib: $(BUILD_DIR)/$(COMPONENT_LIB)
|
||||
|
||||
$(foreach cfile, $(CFILES), $(eval $(call COMPILE_C, $(cfile))))
|
||||
$(foreach cxxfile, $(CPPFILES), $(eval $(call COMPILE_CPP, $(cxxfile))))
|
||||
|
||||
# Create target for building this component as a test
|
||||
TEST_SOURCE_FILES = \
|
||||
test_wl.cpp \
|
||||
main.cpp \
|
||||
|
||||
TEST_OBJ_FILES = $(filter %.o, $(TEST_SOURCE_FILES:.cpp=.o) $(TEST_SOURCE_FILES:.c=.o))
|
||||
|
||||
$(TEST_PROGRAM): lib $(TEST_OBJ_FILES) $(SPI_FLASH_SIM_BUILD_DIR)/$(SPI_FLASH_SIM_LIB) $(STUBS_LIB_BUILD_DIR)/$(STUBS_LIB) partition_table.bin $(SDKCONFIG)
|
||||
g++ $(LDFLAGS) $(CXXFLAGS) -o $@ $(TEST_OBJ_FILES) -L$(BUILD_DIR) -l:$(COMPONENT_LIB) -L$(SPI_FLASH_SIM_BUILD_DIR) -l:$(SPI_FLASH_SIM_LIB) -L$(STUBS_LIB_BUILD_DIR) -l:$(STUBS_LIB)
|
||||
|
||||
test: $(TEST_PROGRAM)
|
||||
./$(TEST_PROGRAM)
|
||||
|
||||
# Create other necessary targets
|
||||
partition_table.bin: partition_table.csv
|
||||
python ../../../components/partition_table/gen_esp32part.py --verify $< $@
|
||||
|
||||
force:
|
||||
|
||||
.PHONY: all lib test clean force
|
36
components/wear_levelling/test_wl_host/Makefile.files
Normal file
36
components/wear_levelling/test_wl_host/Makefile.files
Normal file
@ -0,0 +1,36 @@
|
||||
SOURCE_FILES := \
|
||||
$(addprefix ../, \
|
||||
wear_levelling.cpp \
|
||||
crc32.cpp \
|
||||
WL_Flash.cpp \
|
||||
Partition.cpp \
|
||||
)
|
||||
|
||||
INCLUDE_DIRS := \
|
||||
. \
|
||||
../ \
|
||||
../include \
|
||||
../private_include \
|
||||
../../spi_flash/sim \
|
||||
$(addprefix ../../spi_flash/sim/stubs/, \
|
||||
app_update/include \
|
||||
driver/include \
|
||||
esp32/include \
|
||||
freertos/include \
|
||||
log/include \
|
||||
newlib/include \
|
||||
sdmmc/include \
|
||||
vfs/include \
|
||||
) \
|
||||
$(addprefix ../../../components/, \
|
||||
esp_rom/include \
|
||||
esp_common/include \
|
||||
xtensa/include \
|
||||
xtensa/esp32/include \
|
||||
soc/esp32/include \
|
||||
soc/include \
|
||||
esp32/include \
|
||||
bootloader_support/include \
|
||||
app_update/include \
|
||||
spi_flash/include \
|
||||
)
|
17
components/wear_levelling/test_wl_host/component.mk
Normal file
17
components/wear_levelling/test_wl_host/component.mk
Normal file
@ -0,0 +1,17 @@
|
||||
include $(COMPONENT_PATH)/Makefile.files
|
||||
|
||||
COMPONENT_OWNBUILDTARGET := 1
|
||||
COMPONENT_OWNCLEANTARGET := 1
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := $(INCLUDE_DIRS)
|
||||
|
||||
.PHONY: build
|
||||
build: $(SDKCONFIG_HEADER)
|
||||
$(MAKE) -C $(COMPONENT_PATH) lib SDKCONFIG=$(SDKCONFIG_HEADER) BUILD_DIR=$(COMPONENT_BUILD_DIR) COMPONENT=$(COMPONENT_NAME)
|
||||
|
||||
CLEAN_FILES := component_project_vars.mk
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(summary) RM $(CLEAN_FILES)
|
||||
rm -f $(CLEAN_FILES)
|
||||
$(MAKE) -C $(COMPONENT_PATH) clean SDKCONFIG=$(SDKCONFIG_HEADER) BUILD_DIR=$(COMPONENT_BUILD_DIR) COMPONENT=$(COMPONENT_NAME)
|
@ -0,0 +1,14 @@
|
||||
#include "catch.hpp"
|
||||
#include "esp_err.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression)
|
||||
{
|
||||
printf("ESP_ERROR_CHECK failed: esp_err_t 0x%x", rc);
|
||||
#ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP
|
||||
printf(" (%s)", esp_err_to_name(rc));
|
||||
#endif //CONFIG_ESP_ERR_TO_NAME_LOOKUP
|
||||
printf(" at %p\n", __builtin_return_address(0));
|
||||
printf("file: \"%s\" line %d\nfunc: %s\nexpression: %s\n", file, line, function, expression);
|
||||
abort();
|
||||
}
|
2
components/wear_levelling/test_wl_host/main.cpp
Normal file
2
components/wear_levelling/test_wl_host/main.cpp
Normal file
@ -0,0 +1,2 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch.hpp"
|
@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage, data, , , 1M,
|
|
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#define CONFIG_IDF_TARGET_ESP32 1
|
||||
#define CONFIG_WL_SECTOR_SIZE 4096
|
||||
#define CONFIG_LOG_DEFAULT_LEVEL 3
|
||||
#define CONFIG_PARTITION_TABLE_OFFSET 0x8000
|
||||
#define CONFIG_ESPTOOLPY_FLASHSIZE "8MB"
|
||||
//currently use the legacy implementation, since the stubs for new HAL are not done yet
|
||||
#define CONFIG_SPI_FLASH_USE_LEGACY_IMPL 1
|
||||
|
210
components/wear_levelling/test_wl_host/test_wl.cpp
Normal file
210
components/wear_levelling/test_wl_host/test_wl.cpp
Normal file
@ -0,0 +1,210 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_spi_flash.h"
|
||||
#include "esp_partition.h"
|
||||
#include "wear_levelling.h"
|
||||
#include "WL_Flash.h"
|
||||
#include "SpiFlash.h"
|
||||
|
||||
#include "catch.hpp"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
extern "C" void _spi_flash_init(const char* chip_size, size_t block_size, size_t sector_size, size_t page_size, const char* partition_bin);
|
||||
extern SpiFlash spiflash;
|
||||
|
||||
#define TEST_COUNT_MAX 100
|
||||
|
||||
TEST_CASE("write and read back data", "[wear_levelling]")
|
||||
{
|
||||
_spi_flash_init(CONFIG_ESPTOOLPY_FLASHSIZE, CONFIG_WL_SECTOR_SIZE * 16, CONFIG_WL_SECTOR_SIZE, CONFIG_WL_SECTOR_SIZE, "partition_table.bin");
|
||||
|
||||
esp_err_t result;
|
||||
wl_handle_t wl_handle;
|
||||
|
||||
int flash_handle;
|
||||
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
|
||||
|
||||
// Mount wear-levelled partition
|
||||
result = wl_mount(partition, &wl_handle);
|
||||
REQUIRE(result == ESP_OK);
|
||||
|
||||
// Get the sector size
|
||||
uint32_t sector_size = wl_sector_size(wl_handle);
|
||||
REQUIRE(sector_size == CONFIG_WL_SECTOR_SIZE);
|
||||
|
||||
uint8_t* data = (uint8_t*) malloc(partition->size);
|
||||
uint8_t* read = (uint8_t*) malloc(partition->size);
|
||||
|
||||
uint32_t sectors = partition->size / sector_size;
|
||||
|
||||
// Generate data
|
||||
for(uint32_t sector = 0; sector < sectors; sector++)
|
||||
{
|
||||
uint32_t sector_address = sector * sector_size;
|
||||
|
||||
for(uint32_t i = 0; i < sector_size / sizeof(i); i++)
|
||||
{
|
||||
((uint32_t*) data)[i] = sector_address + i;
|
||||
}
|
||||
}
|
||||
|
||||
// Write data
|
||||
result = wl_write(wl_handle, 0, data, partition->size);
|
||||
REQUIRE(result == ESP_OK);
|
||||
|
||||
// Read data
|
||||
result = wl_read(wl_handle, 0, read, partition->size);
|
||||
REQUIRE(result == ESP_OK);
|
||||
|
||||
// Verify that written and read data match
|
||||
REQUIRE(memcmp(data, read, partition->size));
|
||||
|
||||
// Erase some ranges
|
||||
result = wl_erase_range(wl_handle, 0, sector_size);
|
||||
REQUIRE(result == ESP_OK);
|
||||
result = wl_erase_range(wl_handle, 12288, sector_size * 2);
|
||||
REQUIRE(result == ESP_OK);
|
||||
result = wl_erase_range(wl_handle, 28672, sector_size * 3);
|
||||
REQUIRE(result == ESP_OK);
|
||||
|
||||
// Expected data after erasure
|
||||
memset(data + 0, 0xFF, sector_size);
|
||||
memset(data + 12288, 0xFF, sector_size * 2);
|
||||
memset(data + 28672, 0xFF, sector_size * 3);
|
||||
|
||||
// Read again, with erased ranges
|
||||
result = wl_read(wl_handle, 0, read, partition->size);
|
||||
REQUIRE(result == ESP_OK);
|
||||
|
||||
// Verify that written and read data match
|
||||
REQUIRE(memcmp(data, read, partition->size));
|
||||
|
||||
// Unmount
|
||||
result = wl_unmount(wl_handle);
|
||||
REQUIRE(result == ESP_OK);
|
||||
|
||||
free(data);
|
||||
free(read);
|
||||
}
|
||||
|
||||
TEST_CASE("power down test", "[wear_levelling]")
|
||||
{
|
||||
_spi_flash_init(CONFIG_ESPTOOLPY_FLASHSIZE, CONFIG_WL_SECTOR_SIZE * 16, CONFIG_WL_SECTOR_SIZE, CONFIG_WL_SECTOR_SIZE, "partition_table.bin");
|
||||
|
||||
esp_err_t result;
|
||||
wl_handle_t wl_handle;
|
||||
|
||||
int flash_handle;
|
||||
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
|
||||
|
||||
// Mount wear-levelled partition
|
||||
result = wl_mount(partition, &wl_handle);
|
||||
REQUIRE(result == ESP_OK);
|
||||
|
||||
// Get wl partition information
|
||||
size_t sector_size = wl_sector_size(wl_handle);
|
||||
int32_t sectors_count = wl_size(wl_handle) / sector_size;
|
||||
|
||||
uint32_t add_const = 0;
|
||||
uint32_t *sector_data = new uint32_t[sector_size / sizeof(uint32_t)];
|
||||
|
||||
// Fill partition with check data
|
||||
for (int32_t i = 0; i < sectors_count; i++) {
|
||||
REQUIRE(wl_erase_range(wl_handle, i * sector_size, sector_size) == ESP_OK);
|
||||
for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) {
|
||||
uint32_t temp_data = i * sector_size + add_const + m;
|
||||
sector_data[m] = temp_data;
|
||||
}
|
||||
REQUIRE(wl_write(wl_handle, i * sector_size, sector_data, sector_size) == ESP_OK);
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < sectors_count; i++) {
|
||||
result |= wl_read(wl_handle, i * sector_size, sector_data, sector_size);
|
||||
for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) {
|
||||
uint32_t temp_data = i * sector_size + add_const + m;
|
||||
REQUIRE(temp_data == sector_data[m]);
|
||||
if (temp_data != sector_data[m]) {
|
||||
printf("Error - read: %08x, expected %08x\n", sector_data[m], temp_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perform test
|
||||
int32_t max_count = 100;
|
||||
int32_t max_check_count = TEST_COUNT_MAX;
|
||||
|
||||
printf("used_sectors_count=%d\n", max_check_count);
|
||||
|
||||
for (int32_t k = 0; k < max_check_count; k++) {
|
||||
|
||||
spiflash.set_total_erase_cycles_limit(max_count);
|
||||
|
||||
int32_t err_sector = -1;
|
||||
for (int32_t i = 0; i < sectors_count; i++) {
|
||||
result = ESP_OK;
|
||||
result = wl_erase_range(wl_handle, i * sector_size, sector_size);
|
||||
if (result != ESP_OK) {
|
||||
err_sector = i;
|
||||
break;
|
||||
}
|
||||
for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) {
|
||||
uint32_t temp_data = i * sector_size + add_const + m;
|
||||
sector_data[m] = temp_data;
|
||||
}
|
||||
result = wl_write(wl_handle, i * sector_size, sector_data, sector_size);
|
||||
if (result != ESP_OK) {
|
||||
err_sector = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (err_sector >= 0) {
|
||||
max_count++;
|
||||
} else {
|
||||
max_count = 0;
|
||||
}
|
||||
|
||||
spiflash.set_total_erase_cycles_limit(0);
|
||||
|
||||
result = wl_unmount(wl_handle);
|
||||
REQUIRE(result == ESP_OK);
|
||||
|
||||
result = wl_mount(partition, &wl_handle);
|
||||
REQUIRE(result == ESP_OK);
|
||||
|
||||
for (int32_t i = 0; i < sectors_count; i++) {
|
||||
if (i != err_sector) {
|
||||
result |= wl_read(wl_handle, i * sector_size, sector_data, sector_size);
|
||||
for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) {
|
||||
uint32_t temp_data = i * sector_size + add_const + m;
|
||||
REQUIRE(temp_data == sector_data[m]);
|
||||
if (temp_data != sector_data[m]) {
|
||||
printf("Error - read: %08x, expected %08x, m=%i, sector=%i\n", sector_data[m], temp_data, m, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (err_sector != -1) {
|
||||
result |= wl_erase_range(wl_handle, err_sector * sector_size, sector_size);
|
||||
for (uint32_t m = 0; m < sector_size / sizeof(uint32_t); m++) {
|
||||
uint32_t temp_data = err_sector * sector_size + add_const + m;
|
||||
sector_data[m] = temp_data;
|
||||
}
|
||||
result |= wl_write(wl_handle, err_sector * sector_size, sector_data, sector_size);
|
||||
}
|
||||
|
||||
spiflash.reset_total_erase_cycles();
|
||||
|
||||
printf("[%3.f%%] err_sector=%i\n", (float)k / ((float)max_check_count) * 100.0f, err_sector);
|
||||
}
|
||||
|
||||
delete[] sector_data;
|
||||
|
||||
// Unmount
|
||||
result = wl_unmount(wl_handle);
|
||||
REQUIRE(result == ESP_OK);
|
||||
}
|
267
components/wear_levelling/wear_levelling.cpp
Normal file
267
components/wear_levelling/wear_levelling.cpp
Normal file
@ -0,0 +1,267 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <new>
|
||||
#include <sys/lock.h>
|
||||
#include "wear_levelling.h"
|
||||
#include "WL_Config.h"
|
||||
#include "WL_Ext_Cfg.h"
|
||||
#include "WL_Flash.h"
|
||||
#include "WL_Ext_Perf.h"
|
||||
#include "WL_Ext_Safe.h"
|
||||
#include "SPI_Flash.h"
|
||||
#include "Partition.h"
|
||||
|
||||
#ifndef MAX_WL_HANDLES
|
||||
#define MAX_WL_HANDLES 8
|
||||
#endif // MAX_WL_HANDLES
|
||||
|
||||
#ifndef WL_DEFAULT_UPDATERATE
|
||||
#define WL_DEFAULT_UPDATERATE 16
|
||||
#endif //WL_DEFAULT_UPDATERATE
|
||||
|
||||
#ifndef WL_DEFAULT_TEMP_BUFF_SIZE
|
||||
#define WL_DEFAULT_TEMP_BUFF_SIZE 32
|
||||
#endif //WL_DEFAULT_TEMP_BUFF_SIZE
|
||||
|
||||
#ifndef WL_DEFAULT_WRITE_SIZE
|
||||
#define WL_DEFAULT_WRITE_SIZE 16
|
||||
#endif //WL_DEFAULT_WRITE_SIZE
|
||||
|
||||
#ifndef WL_DEFAULT_START_ADDR
|
||||
#define WL_DEFAULT_START_ADDR 0
|
||||
#endif //WL_DEFAULT_START_ADDR
|
||||
|
||||
#ifndef WL_CURRENT_VERSION
|
||||
#define WL_CURRENT_VERSION 2
|
||||
#endif //WL_CURRENT_VERSION
|
||||
|
||||
typedef struct {
|
||||
WL_Flash *instance;
|
||||
_lock_t lock;
|
||||
} wl_instance_t;
|
||||
|
||||
static wl_instance_t s_instances[MAX_WL_HANDLES];
|
||||
static _lock_t s_instances_lock;
|
||||
static const char *TAG = "wear_levelling";
|
||||
|
||||
static esp_err_t check_handle(wl_handle_t handle, const char *func);
|
||||
|
||||
esp_err_t wl_mount(const esp_partition_t *partition, wl_handle_t *out_handle)
|
||||
{
|
||||
// Initialize variables before the first jump to cleanup label
|
||||
void *wl_flash_ptr = NULL;
|
||||
WL_Flash *wl_flash = NULL;
|
||||
void *part_ptr = NULL;
|
||||
Partition *part = NULL;
|
||||
|
||||
_lock_acquire(&s_instances_lock);
|
||||
esp_err_t result = ESP_OK;
|
||||
*out_handle = WL_INVALID_HANDLE;
|
||||
for (size_t i = 0; i < MAX_WL_HANDLES; i++) {
|
||||
if (s_instances[i].instance == NULL) {
|
||||
*out_handle = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
wl_ext_cfg_t cfg;
|
||||
cfg.full_mem_size = partition->size;
|
||||
cfg.start_addr = WL_DEFAULT_START_ADDR;
|
||||
cfg.version = WL_CURRENT_VERSION;
|
||||
cfg.sector_size = SPI_FLASH_SEC_SIZE;
|
||||
cfg.page_size = SPI_FLASH_SEC_SIZE;
|
||||
cfg.updaterate = WL_DEFAULT_UPDATERATE;
|
||||
cfg.temp_buff_size = WL_DEFAULT_TEMP_BUFF_SIZE;
|
||||
cfg.wr_size = WL_DEFAULT_WRITE_SIZE;
|
||||
// FAT sector size by default will be 512
|
||||
cfg.fat_sector_size = CONFIG_WL_SECTOR_SIZE;
|
||||
|
||||
if (*out_handle == WL_INVALID_HANDLE) {
|
||||
ESP_LOGE(TAG, "MAX_WL_HANDLES=%d instances already allocated", MAX_WL_HANDLES);
|
||||
result = ESP_ERR_NO_MEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// Allocate memory for a Partition object, and then initialize the object
|
||||
// using placement new operator. This way we can recover from out of
|
||||
// memory condition.
|
||||
part_ptr = malloc(sizeof(Partition));
|
||||
if (part_ptr == NULL) {
|
||||
result = ESP_ERR_NO_MEM;
|
||||
ESP_LOGE(TAG, "%s: can't allocate Partition", __func__);
|
||||
goto out;
|
||||
}
|
||||
part = new (part_ptr) Partition(partition);
|
||||
|
||||
// Same for WL_Flash: allocate memory, use placement new
|
||||
#if CONFIG_WL_SECTOR_SIZE == 512
|
||||
#if CONFIG_WL_SECTOR_MODE == 1
|
||||
wl_flash_ptr = malloc(sizeof(WL_Ext_Safe));
|
||||
|
||||
if (wl_flash_ptr == NULL) {
|
||||
result = ESP_ERR_NO_MEM;
|
||||
ESP_LOGE(TAG, "%s: can't allocate WL_Ext_Safe", __func__);
|
||||
goto out;
|
||||
}
|
||||
wl_flash = new (wl_flash_ptr) WL_Ext_Safe();
|
||||
#else
|
||||
wl_flash_ptr = malloc(sizeof(WL_Ext_Perf));
|
||||
|
||||
if (wl_flash_ptr == NULL) {
|
||||
result = ESP_ERR_NO_MEM;
|
||||
ESP_LOGE(TAG, "%s: can't allocate WL_Ext_Perf", __func__);
|
||||
goto out;
|
||||
}
|
||||
wl_flash = new (wl_flash_ptr) WL_Ext_Perf();
|
||||
#endif // CONFIG_WL_SECTOR_MODE
|
||||
#endif // CONFIG_WL_SECTOR_SIZE
|
||||
#if CONFIG_WL_SECTOR_SIZE == 4096
|
||||
wl_flash_ptr = malloc(sizeof(WL_Flash));
|
||||
|
||||
if (wl_flash_ptr == NULL) {
|
||||
result = ESP_ERR_NO_MEM;
|
||||
ESP_LOGE(TAG, "%s: can't allocate WL_Flash", __func__);
|
||||
goto out;
|
||||
}
|
||||
wl_flash = new (wl_flash_ptr) WL_Flash();
|
||||
#endif // CONFIG_WL_SECTOR_SIZE
|
||||
|
||||
result = wl_flash->config(&cfg, part);
|
||||
if (ESP_OK != result) {
|
||||
ESP_LOGE(TAG, "%s: config instance=0x%08x, result=0x%x", __func__, *out_handle, result);
|
||||
goto out;
|
||||
}
|
||||
result = wl_flash->init();
|
||||
if (ESP_OK != result) {
|
||||
ESP_LOGE(TAG, "%s: init instance=0x%08x, result=0x%x", __func__, *out_handle, result);
|
||||
goto out;
|
||||
}
|
||||
s_instances[*out_handle].instance = wl_flash;
|
||||
_lock_init(&s_instances[*out_handle].lock);
|
||||
_lock_release(&s_instances_lock);
|
||||
return ESP_OK;
|
||||
|
||||
out:
|
||||
_lock_release(&s_instances_lock);
|
||||
*out_handle = WL_INVALID_HANDLE;
|
||||
if (wl_flash) {
|
||||
wl_flash->~WL_Flash();
|
||||
free(wl_flash);
|
||||
}
|
||||
if (part) {
|
||||
part->~Partition();
|
||||
free(part);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t wl_unmount(wl_handle_t handle)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
_lock_acquire(&s_instances_lock);
|
||||
result = check_handle(handle, __func__);
|
||||
if (result == ESP_OK) {
|
||||
// We have to flush state of the component
|
||||
result = s_instances[handle].instance->flush();
|
||||
// We use placement new in wl_mount, so call destructor directly
|
||||
Flash_Access *drv = s_instances[handle].instance->get_drv();
|
||||
drv->~Flash_Access();
|
||||
free(drv);
|
||||
s_instances[handle].instance->~WL_Flash();
|
||||
free(s_instances[handle].instance);
|
||||
s_instances[handle].instance = NULL;
|
||||
_lock_close(&s_instances[handle].lock); // also zeroes the lock variable
|
||||
}
|
||||
_lock_release(&s_instances_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t wl_erase_range(wl_handle_t handle, size_t start_addr, size_t size)
|
||||
{
|
||||
esp_err_t result = check_handle(handle, __func__);
|
||||
if (result != ESP_OK) {
|
||||
return result;
|
||||
}
|
||||
_lock_acquire(&s_instances[handle].lock);
|
||||
result = s_instances[handle].instance->erase_range(start_addr, size);
|
||||
_lock_release(&s_instances[handle].lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t wl_write(wl_handle_t handle, size_t dest_addr, const void *src, size_t size)
|
||||
{
|
||||
esp_err_t result = check_handle(handle, __func__);
|
||||
if (result != ESP_OK) {
|
||||
return result;
|
||||
}
|
||||
_lock_acquire(&s_instances[handle].lock);
|
||||
result = s_instances[handle].instance->write(dest_addr, src, size);
|
||||
_lock_release(&s_instances[handle].lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t wl_read(wl_handle_t handle, size_t src_addr, void *dest, size_t size)
|
||||
{
|
||||
esp_err_t result = check_handle(handle, __func__);
|
||||
if (result != ESP_OK) {
|
||||
return result;
|
||||
}
|
||||
_lock_acquire(&s_instances[handle].lock);
|
||||
result = s_instances[handle].instance->read(src_addr, dest, size);
|
||||
_lock_release(&s_instances[handle].lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t wl_size(wl_handle_t handle)
|
||||
{
|
||||
esp_err_t err = check_handle(handle, __func__);
|
||||
if (err != ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
_lock_acquire(&s_instances[handle].lock);
|
||||
size_t result = s_instances[handle].instance->chip_size();
|
||||
_lock_release(&s_instances[handle].lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t wl_sector_size(wl_handle_t handle)
|
||||
{
|
||||
esp_err_t err = check_handle(handle, __func__);
|
||||
if (err != ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
_lock_acquire(&s_instances[handle].lock);
|
||||
size_t result = s_instances[handle].instance->sector_size();
|
||||
_lock_release(&s_instances[handle].lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
static esp_err_t check_handle(wl_handle_t handle, const char *func)
|
||||
{
|
||||
if (handle == WL_INVALID_HANDLE) {
|
||||
ESP_LOGE(TAG, "%s: invalid handle", func);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
if (handle >= MAX_WL_HANDLES) {
|
||||
ESP_LOGE(TAG, "%s: instance[0x%08x] out of range", func, handle);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (s_instances[handle].instance == NULL) {
|
||||
ESP_LOGE(TAG, "%s: instance[0x%08x] not initialized", func, handle);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
Reference in New Issue
Block a user