Merge branch 'feature/phy_init' into 'master'

PHY init support

This change set adds PHY init support and fixes NVS initialization.

Some configuration options for PHY init process are added to menuconfig.


See merge request !206
This commit is contained in:
Ivan Grokhotkov 2016-11-18 20:46:03 +08:00
commit 5a81c06e39
24 changed files with 990 additions and 103 deletions

View File

@ -364,4 +364,43 @@ config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
depends on DOCUMENTATION_FOR_RTC_CNTL depends on DOCUMENTATION_FOR_RTC_CNTL
endchoice endchoice
config ESP32_PHY_AUTO_INIT
bool "Initialize PHY in startup code"
default y
help
If enabled, PHY will be initialized in startup code, before
app_main function runs.
If this is undesired, disable this option and call esp_phy_init
from the application before enabling WiFi or BT.
If this option is enabled, startup code will also initialize
NVS prior to initializing PHY.
If unsure, choose 'y'.
config ESP32_PHY_INIT_DATA_IN_PARTITION
bool "Use a partition to store PHY init data"
default n
help
If enabled, PHY init data will be loaded from a partition.
When using a custom partition table, make sure that PHY data
partition is included (type: 'data', subtype: 'phy').
With default partition tables, this is done automatically.
If PHY init data is stored in a partition, it has to be flashed there,
otherwise runtime error will occur.
If this option is not enabled, PHY init data will be embedded
into the application binary.
If unsure, choose 'n'.
config ESP32_PHY_MAX_TX_POWER
int "Max TX power (dBm)"
range 0 20
default 20
help
Set maximum transmit power. Actual transmit power for high
data rates may be lower than this setting.
endmenu endmenu

View File

@ -0,0 +1,32 @@
ifdef CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION
PHY_INIT_DATA_OBJ = $(BUILD_DIR_BASE)/phy_init_data.o
PHY_INIT_DATA_BIN = $(BUILD_DIR_BASE)/phy_init_data.bin
# Command to flash PHY init data partition
PHY_INIT_DATA_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(CONFIG_PHY_DATA_OFFSET) $(PHY_INIT_DATA_BIN)
ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_PHY_DATA_OFFSET) $(PHY_INIT_DATA_BIN)
ESP32_COMPONENT_PATH := $(COMPONENT_PATH)
$(PHY_INIT_DATA_OBJ): $(ESP32_COMPONENT_PATH)/phy_init_data.h $(BUILD_DIR_BASE)/include/sdkconfig.h
$(summary) CC $(notdir $@)
printf "#include \"phy_init_data.h\"\n" | $(CC) -I $(BUILD_DIR_BASE)/include -I $(ESP32_COMPONENT_PATH) -I $(ESP32_COMPONENT_PATH)/include -c -o $@ -xc -
$(PHY_INIT_DATA_BIN): $(PHY_INIT_DATA_OBJ)
$(summary) BIN $(notdir $@)
$(OBJCOPY) -O binary $< $@
phy_init_data: $(PHY_INIT_DATA_BIN)
phy_init_data-flash: $(BUILD_DIR_BASE)/phy_init_data.bin
@echo "Flashing PHY init data..."
$(PHY_INIT_DATA_FLASH_CMD)
phy_init_data-clean:
rm -f $(PHY_INIT_DATA_BIN) $(PHY_INIT_DATA_OBJ)
all: phy_init_data
flash: phy_init_data
endif # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION

View File

@ -50,6 +50,7 @@
#include "esp_brownout.h" #include "esp_brownout.h"
#include "esp_int_wdt.h" #include "esp_int_wdt.h"
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include "esp_phy_init.h"
#include "trax.h" #include "trax.h"
void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))); void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default")));
@ -62,6 +63,7 @@ static bool app_cpu_started = false;
#endif //!CONFIG_FREERTOS_UNICORE #endif //!CONFIG_FREERTOS_UNICORE
static void do_global_ctors(void); static void do_global_ctors(void);
static void do_phy_init();
static void main_task(void* args); static void main_task(void* args);
extern void app_main(void); extern void app_main(void);
@ -187,6 +189,11 @@ void start_cpu0_default(void)
esp_ipc_init(); esp_ipc_init();
spi_flash_init(); spi_flash_init();
#if CONFIG_ESP32_PHY_AUTO_INIT
nvs_flash_init();
do_phy_init();
#endif
xTaskCreatePinnedToCore(&main_task, "main", xTaskCreatePinnedToCore(&main_task, "main",
ESP_TASK_MAIN_STACK, NULL, ESP_TASK_MAIN_STACK, NULL,
ESP_TASK_MAIN_PRIO, NULL, 0); ESP_TASK_MAIN_PRIO, NULL, 0);
@ -224,3 +231,36 @@ static void main_task(void* args)
vTaskDelete(NULL); vTaskDelete(NULL);
} }
static void do_phy_init()
{
esp_phy_calibration_mode_t calibration_mode = PHY_RF_CAL_PARTIAL;
if (rtc_get_reset_reason(0) == DEEPSLEEP_RESET) {
calibration_mode = PHY_RF_CAL_NONE;
}
const esp_phy_init_data_t* init_data = esp_phy_get_init_data();
if (init_data == NULL) {
ESP_LOGE(TAG, "failed to obtain PHY init data");
abort();
}
esp_phy_calibration_data_t* cal_data =
(esp_phy_calibration_data_t*) calloc(sizeof(esp_phy_calibration_data_t), 1);
if (cal_data == NULL) {
ESP_LOGE(TAG, "failed to allocate memory for RF calibration data");
abort();
}
esp_err_t err = esp_phy_load_cal_data_from_nvs(cal_data);
if (err != ESP_OK) {
ESP_LOGW(TAG, "failed to load RF calibration data, falling back to full calibration");
calibration_mode = PHY_RF_CAL_FULL;
}
esp_phy_init(init_data, calibration_mode, cal_data);
if (calibration_mode != PHY_RF_CAL_NONE) {
err = esp_phy_store_cal_data_to_nvs(cal_data);
} else {
err = ESP_OK;
}
esp_phy_release_init_data(init_data);
free(cal_data); // PHY maintains a copy of calibration data, so we can free this
}

View File

@ -0,0 +1,249 @@
// 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.
#pragma once
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file PHY init parameters and API
*/
/**
* @brief Structure holding PHY init parameters
*/
typedef struct {
uint8_t param_ver_id; /*!< init_data structure version */
uint8_t crystal_select; /*!< 0: 40MHz, 1: 26 MHz, 2: 24 MHz, 3: auto */
uint8_t wifi_rx_gain_swp_step_1; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_2; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_3; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_4; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_5; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_6; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_7; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_8; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_9; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_10; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_11; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_12; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_13; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_14; /*!< do not change */
uint8_t wifi_rx_gain_swp_step_15; /*!< do not change */
uint8_t bt_rx_gain_swp_step_1; /*!< do not change */
uint8_t bt_rx_gain_swp_step_2; /*!< do not change */
uint8_t bt_rx_gain_swp_step_3; /*!< do not change */
uint8_t bt_rx_gain_swp_step_4; /*!< do not change */
uint8_t bt_rx_gain_swp_step_5; /*!< do not change */
uint8_t bt_rx_gain_swp_step_6; /*!< do not change */
uint8_t bt_rx_gain_swp_step_7; /*!< do not change */
uint8_t bt_rx_gain_swp_step_8; /*!< do not change */
uint8_t bt_rx_gain_swp_step_9; /*!< do not change */
uint8_t bt_rx_gain_swp_step_10; /*!< do not change */
uint8_t bt_rx_gain_swp_step_11; /*!< do not change */
uint8_t bt_rx_gain_swp_step_12; /*!< do not change */
uint8_t bt_rx_gain_swp_step_13; /*!< do not change */
uint8_t bt_rx_gain_swp_step_14; /*!< do not change */
uint8_t bt_rx_gain_swp_step_15; /*!< do not change */
uint8_t gain_cmp_1; /*!< do not change */
uint8_t gain_cmp_6; /*!< do not change */
uint8_t gain_cmp_11; /*!< do not change */
uint8_t gain_cmp_ext2_1; /*!< do not change */
uint8_t gain_cmp_ext2_6; /*!< do not change */
uint8_t gain_cmp_ext2_11; /*!< do not change */
uint8_t gain_cmp_ext3_1; /*!< do not change */
uint8_t gain_cmp_ext3_6; /*!< do not change */
uint8_t gain_cmp_ext3_11; /*!< do not change */
uint8_t gain_cmp_bt_ofs_1; /*!< do not change */
uint8_t gain_cmp_bt_ofs_6; /*!< do not change */
uint8_t gain_cmp_bt_ofs_11; /*!< do not change */
uint8_t target_power_qdb_0; /*!< 78 means target power is 78/4=19.5dbm */
uint8_t target_power_qdb_1; /*!< 76 means target power is 76/4=19dbm */
uint8_t target_power_qdb_2; /*!< 74 means target power is 74/4=18.5dbm */
uint8_t target_power_qdb_3; /*!< 68 means target power is 68/4=17dbm */
uint8_t target_power_qdb_4; /*!< 64 means target power is 64/4=16dbm */
uint8_t target_power_qdb_5; /*!< 52 means target power is 52/4=13dbm */
uint8_t target_power_index_mcs0; /*!< target power index is 0, means target power is target_power_qdb_0 19.5dbm; (1m,2m,5.5m,11m,6m,9m) */
uint8_t target_power_index_mcs1; /*!< target power index is 0, means target power is target_power_qdb_0 19.5dbm; (12m) */
uint8_t target_power_index_mcs2; /*!< target power index is 1, means target power is target_power_qdb_1 19dbm; (18m) */
uint8_t target_power_index_mcs3; /*!< target power index is 1, means target power is target_power_qdb_1 19dbm; (24m) */
uint8_t target_power_index_mcs4; /*!< target power index is 2, means target power is target_power_qdb_2 18.5dbm; (36m) */
uint8_t target_power_index_mcs5; /*!< target power index is 3, means target power is target_power_qdb_3 17dbm; (48m) */
uint8_t target_power_index_mcs6; /*!< target power index is 4, means target power is target_power_qdb_4 16dbm; (54m) */
uint8_t target_power_index_mcs7; /*!< target power index is 5, means target power is target_power_qdb_5 13dbm */
uint8_t pwr_ind_11b_en; /*!< 0: 11b power is same as mcs0 and 6m, 1: 11b power different with OFDM */
uint8_t pwr_ind_11b_0; /*!< 1m, 2m power index [0~5] */
uint8_t pwr_ind_11b_1; /*!< 5.5m, 11m power index [0~5] */
uint8_t chan_backoff_en; /*!< 0: channel backoff disable, 1:channel backoff enable */
uint8_t chan1_power_backoff_qdb; /*!< 4 means backoff is 1db */
uint8_t chan2_power_backoff_qdb; /*!< see chan1_power_backoff_qdb */
uint8_t chan3_power_backoff_qdb; /*!< chan1_power_backoff_qdb */
uint8_t chan4_power_backoff_qdb; /*!< chan1_power_backoff_qdb */
uint8_t chan5_power_backoff_qdb; /*!< chan1_power_backoff_qdb */
uint8_t chan6_power_backoff_qdb; /*!< chan1_power_backoff_qdb */
uint8_t chan7_power_backoff_qdb; /*!< chan1_power_backoff_qdb */
uint8_t chan8_power_backoff_qdb; /*!< chan1_power_backoff_qdb */
uint8_t chan9_power_backoff_qdb; /*!< chan1_power_backoff_qdb */
uint8_t chan10_power_backoff_qdb; /*!< chan1_power_backoff_qdb */
uint8_t chan11_power_backoff_qdb; /*!< chan1_power_backoff_qdb */
uint8_t chan12_power_backoff_qdb; /*!< chan1_power_backoff_qdb */
uint8_t chan13_power_backoff_qdb; /*!< chan1_power_backoff_qdb */
uint8_t chan14_power_backoff_qdb; /*!< chan1_power_backoff_qdb */
uint8_t chan1_rate_backoff_index; /*!< if bit i is set, backoff data rate is target_power_qdb_i */
uint8_t chan2_rate_backoff_index; /*!< see chan1_rate_backoff_index */
uint8_t chan3_rate_backoff_index; /*!< see chan1_rate_backoff_index */
uint8_t chan4_rate_backoff_index; /*!< see chan1_rate_backoff_index */
uint8_t chan5_rate_backoff_index; /*!< see chan1_rate_backoff_index */
uint8_t chan6_rate_backoff_index; /*!< see chan1_rate_backoff_index */
uint8_t chan7_rate_backoff_index; /*!< see chan1_rate_backoff_index */
uint8_t chan8_rate_backoff_index; /*!< see chan1_rate_backoff_index */
uint8_t chan9_rate_backoff_index; /*!< see chan1_rate_backoff_index */
uint8_t chan10_rate_backoff_index; /*!< see chan1_rate_backoff_index */
uint8_t chan11_rate_backoff_index; /*!< see chan1_rate_backoff_index */
uint8_t chan12_rate_backoff_index; /*!< see chan1_rate_backoff_index */
uint8_t chan13_rate_backoff_index; /*!< see chan1_rate_backoff_index */
uint8_t chan14_rate_backoff_index; /*!< see chan1_rate_backoff_index */
uint8_t spur_freq_cfg_msb_1; /*!< first spur: */
uint8_t spur_freq_cfg_1; /*!< spur_freq_cfg = (spur_freq_cfg_msb_1 <<8) | spur_freq_cfg_1 */
uint8_t spur_freq_cfg_div_1; /*!< spur_freq=spur_freq_cfg/spur_freq_cfg_div_1 */
uint8_t spur_freq_en_h_1; /*!< the seventh bit for total enable */
uint8_t spur_freq_en_l_1; /*!< each bit for 1 channel, and use [spur_freq_en_h, spur_freq_en_l] to select the spur's channel priority */
uint8_t spur_freq_cfg_msb_2; /*!< second spur: */
uint8_t spur_freq_cfg_2; /*!< spur_freq_cfg = (spur_freq_cfg_msb_2 <<8) | spur_freq_cfg_2 */
uint8_t spur_freq_cfg_div_2; /*!< spur_freq=spur_freq_cfg/spur_freq_cfg_div_2 */
uint8_t spur_freq_en_h_2; /*!< the seventh bit for total enable */
uint8_t spur_freq_en_l_2; /*!< each bit for 1 channel, and use [spur_freq_en_h, spur_freq_en_l] to select the spur's channel priority */
uint8_t spur_freq_cfg_msb_3; /*!< third spur: */
uint8_t spur_freq_cfg_3; /*!< spur_freq_cfg = (spur_freq_cfg_msb_3 <<8) | spur_freq_cfg_3 */
uint8_t spur_freq_cfg_div_3; /*!< spur_freq=spur_freq_cfg/spur_freq_cfg_div_3 */
uint8_t spur_freq_en_h_3; /*!< the seventh bit for total enable */
uint8_t spur_freq_en_l_3; /*!< each bit for 1 channel, and use [spur_freq_en_h, spur_freq_en_l] to select the spur's channel priority, */
uint8_t reserved[23]; /*!< reserved for future expansion */
} esp_phy_init_data_t;
/**
* @brief Opaque PHY calibration data
*/
typedef struct {
uint8_t opaque[1904]; /*!< calibration data */
} esp_phy_calibration_data_t;
typedef enum {
PHY_RF_CAL_PARTIAL = 0x00000000, /*!< Do part of RF calibration. This should be used after power-on reset. */
PHY_RF_CAL_NONE = 0x00000001, /*!< Don't do any RF calibration. This mode is only suggested to be used after deep sleep reset. */
PHY_RF_CAL_FULL = 0x00000002 /*!< Do full RF calibration. Produces best results, but also consumes a lot of time and current. Suggested to be used once. */
} esp_phy_calibration_mode_t;
/**
* @brief Get PHY init data
*
* If "Use a partition to store PHY init data" option is set in menuconfig,
* This function will load PHY init data from a partition. Otherwise,
* PHY init data will be compiled into the application itself, and this function
* will return a pointer to PHY init data located in read-only memory (DROM).
*
* If "Use a partition to store PHY init data" option is enabled, this function
* may return NULL if the data loaded from flash is not valid.
*
* @note Call esp_phy_release_init_data to release the pointer obtained using
* this function after the call to esp_wifi_init.
*
* @return pointer to PHY init data structure
*/
const esp_phy_init_data_t* esp_phy_get_init_data();
/**
* @brief Release PHY init data
* @param data pointer to PHY init data structure obtained from
* esp_phy_get_init_data function
*/
void esp_phy_release_init_data(const esp_phy_init_data_t* data);
/**
* @brief Function called by esp_phy_init to load PHY calibration data
*
* This is a convenience function which can be used to load PHY calibration
* data from NVS. Data can be stored to NVS using esp_phy_store_cal_data_to_nvs
* function.
*
* If calibration data is not present in the NVS, or
* data is not valid (was obtained for a chip with a different MAC address,
* or obtained for a different version of software), this function will
* return an error.
*
* If "Initialize PHY in startup code" option is set in menuconfig, this
* function will be used to load calibration data. To provide a different
* mechanism for loading calibration data, disable
* "Initialize PHY in startup code" option in menuconfig and call esp_phy_init
* function from the application. For an example usage of esp_phy_init and
* this function, see do_phy_init function in cpu_start.c
*
* @param out_cal_data pointer to calibration data structure to be filled with
* loaded data.
* @return ESP_OK on success
*/
esp_err_t esp_phy_load_cal_data_from_nvs(esp_phy_calibration_data_t* out_cal_data);
/**
* @brief Function called by esp_phy_init to store PHY calibration data
*
* This is a convenience function which can be used to store PHY calibration
* data to the NVS. Calibration data is returned by esp_phy_init function.
* Data saved using this function to the NVS can later be loaded using
* esp_phy_store_cal_data_to_nvs function.
*
* If "Initialize PHY in startup code" option is set in menuconfig, this
* function will be used to store calibration data. To provide a different
* mechanism for storing calibration data, disable
* "Initialize PHY in startup code" option in menuconfig and call esp_phy_init
* function from the application.
*
* @param cal_data pointer to calibration data which has to be saved.
* @return ESP_OK on success
*/
esp_err_t esp_phy_store_cal_data_to_nvs(const esp_phy_calibration_data_t* cal_data);
/**
* @brief Initialize PHY module
*
* PHY module should be initialized in order to use WiFi or BT.
* If "Initialize PHY in startup code" option is set in menuconfig,
* this function will be called automatically before app_main is called,
* using parameters obtained from esp_phy_get_init_data.
*
* Applications which don't need to enable PHY on every start up should
* disable this menuconfig option and call esp_phy_init before calling
* esp_wifi_init or bt_controller_init. See do_phy_init function in
* cpu_start.c for an example of using this function.
*
* @param init_data PHY parameters. Default set of parameters can
* be obtained by calling esp_phy_get_default_init_data
* function.
* @param mode Calibration mode (Full, partial, or no calibration)
* @param[inout] calibration_data
* @return ESP_OK on success.
*/
esp_err_t esp_phy_init(const esp_phy_init_data_t* init_data,
esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data);
#ifdef __cplusplus
}
#endif

View File

@ -1028,6 +1028,7 @@
#define DPORT_WIFI_RST_EN_REG (DR_REG_DPORT_BASE + 0x0D0) #define DPORT_WIFI_RST_EN_REG (DR_REG_DPORT_BASE + 0x0D0)
/* DPORT_WIFI_RST : R/W ;bitpos:[31:0] ;default: 32'h0 ; */ /* DPORT_WIFI_RST : R/W ;bitpos:[31:0] ;default: 32'h0 ; */
/*description: */ /*description: */
#define DPORT_MAC_RST (BIT(2))
#define DPORT_WIFI_RST 0xFFFFFFFF #define DPORT_WIFI_RST 0xFFFFFFFF
#define DPORT_WIFI_RST_M ((DPORT_WIFI_RST_V)<<(DPORT_WIFI_RST_S)) #define DPORT_WIFI_RST_M ((DPORT_WIFI_RST_V)<<(DPORT_WIFI_RST_S))
#define DPORT_WIFI_RST_V 0xFFFFFFFF #define DPORT_WIFI_RST_V 0xFFFFFFFF

@ -1 +1 @@
Subproject commit e188536a6315cc3ce4f1006ac3a4450faea6abc6 Subproject commit a580f70a64872a7cc291b1f22455f6adbc2e35cf

64
components/esp32/phy.h Normal file
View File

@ -0,0 +1,64 @@
// 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.
#pragma once
#include "esp_phy_init.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file phy.h
* @brief Declarations for functions provided by libphy.a
*/
/**
* @brief Initialize function pointer table in PHY library.
* @note This function should be called before register_chipv7_phy.
*/
void phy_get_romfunc_addr(void);
/**
* @brief Initialize PHY module and do RF calibration
* @param[in] init_data Initialization parameters to be used by the PHY
* @param[inout] cal_data As input, calibration data previously obtained. As output, will contain new calibration data.
* @param[in] cal_mode RF calibration mode
* @return reserved for future use
*/
int register_chipv7_phy(const esp_phy_init_data_t* init_data, esp_phy_calibration_data_t *cal_data, esp_phy_calibration_mode_t cal_mode);
/**
* @brief Get the format version of calibration data used by PHY library.
* @return Format version number, OR'ed with BIT(16) if PHY is in WIFI only mode.
*/
uint32_t phy_get_rf_cal_version();
/**
* @brief Set RF/BB for only WIFI mode or coexist(WIFI & BT) mode
* @param[in] true is for only WIFI mode, false is for coexist mode. default is 0.
* @return NULL
*/
void phy_set_wifi_mode_only(bool wifi_only);
/**
* @brief Set BT the highest priority in coexist mode.
* @return NULL
*/
void coex_bt_high_prio(void);
#ifdef __cplusplus
}
#endif

224
components/esp32/phy_init.c Normal file
View File

@ -0,0 +1,224 @@
// 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 <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "rom/ets_sys.h"
#include "soc/dport_reg.h"
#include "esp_err.h"
#include "esp_phy_init.h"
#include "esp_system.h"
#include "phy.h"
#include "esp_log.h"
#include "nvs.h"
#include "sdkconfig.h"
#include "phy_init_data.h"
static const char* TAG = "phy_init";
esp_err_t esp_phy_init(const esp_phy_init_data_t* init_data,
esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data)
{
assert(init_data);
assert(calibration_data);
// Initialize PHY pointer table
phy_get_romfunc_addr();
REG_SET_BIT(DPORT_WIFI_RST_EN_REG, DPORT_MAC_RST);
REG_CLR_BIT(DPORT_WIFI_RST_EN_REG, DPORT_MAC_RST);
// Enable WiFi peripheral clock
SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, 0x87cf);
ESP_LOGV(TAG, "register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d",
init_data, calibration_data, mode);
phy_set_wifi_mode_only(0);
register_chipv7_phy(init_data, calibration_data, mode);
coex_bt_high_prio();
return ESP_OK;
}
// PHY init data handling functions
#if CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION
#include "esp_partition.h"
const esp_phy_init_data_t* esp_phy_get_init_data()
{
const esp_partition_t* partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_PHY, NULL);
if (partition == NULL) {
ESP_LOGE(TAG, "PHY data partition not found");
return NULL;
}
ESP_LOGD(TAG, "loading PHY init data from partition at offset 0x%x", partition->address);
size_t init_data_store_length = sizeof(phy_init_magic_pre) +
sizeof(esp_phy_init_data_t) + sizeof(phy_init_magic_post);
uint8_t* init_data_store = (uint8_t*) malloc(init_data_store_length);
if (init_data_store == NULL) {
ESP_LOGE(TAG, "failed to allocate memory for PHY init data");
return NULL;
}
esp_err_t err = esp_partition_read(partition, 0, init_data_store, init_data_store_length);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to read PHY data partition (%d)", err);
return NULL;
}
if (memcmp(init_data_store, PHY_INIT_MAGIC, sizeof(phy_init_magic_pre)) != 0 ||
memcmp(init_data_store + init_data_store_length - sizeof(phy_init_magic_post),
PHY_INIT_MAGIC, sizeof(phy_init_magic_post)) != 0) {
ESP_LOGE(TAG, "failed to validate PHY data partition");
return NULL;
}
ESP_LOGE(TAG, "PHY data partition validated");
return (const esp_phy_init_data_t*) (init_data_store + sizeof(phy_init_magic_pre));
}
void esp_phy_release_init_data(const esp_phy_init_data_t* init_data)
{
free((uint8_t*) init_data - sizeof(phy_init_magic_pre));
}
#else // CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION
// phy_init_data.h will declare static 'phy_init_data' variable initialized with default init data
const esp_phy_init_data_t* esp_phy_get_init_data()
{
ESP_LOGD(TAG, "loading PHY init data from application binary");
return &phy_init_data;
}
void esp_phy_release_init_data(const esp_phy_init_data_t* init_data)
{
// no-op
}
#endif // CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION
// PHY calibration data handling functions
static const char* PHY_NAMESPACE = "phy";
static const char* PHY_CAL_VERSION_KEY = "cal_version";
static const char* PHY_CAL_MAC_KEY = "cal_mac";
static const char* PHY_CAL_DATA_KEY = "cal_data";
static esp_err_t load_cal_data_from_nvs_handle(nvs_handle handle,
esp_phy_calibration_data_t* out_cal_data);
static esp_err_t store_cal_data_to_nvs_handle(nvs_handle handle,
const esp_phy_calibration_data_t* cal_data);
esp_err_t esp_phy_load_cal_data_from_nvs(esp_phy_calibration_data_t* out_cal_data)
{
nvs_handle handle;
esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READONLY, &handle);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: failed to open NVS namespace (%d)", __func__, err);
return err;
}
else {
err = load_cal_data_from_nvs_handle(handle, out_cal_data);
nvs_close(handle);
return err;
}
}
esp_err_t esp_phy_store_cal_data_to_nvs(const esp_phy_calibration_data_t* cal_data)
{
nvs_handle handle;
esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: failed to open NVS namespace (%d)", __func__, err);
return err;
}
else {
err = store_cal_data_to_nvs_handle(handle, cal_data);
nvs_close(handle);
return err;
}
}
static esp_err_t load_cal_data_from_nvs_handle(nvs_handle handle,
esp_phy_calibration_data_t* out_cal_data)
{
esp_err_t err;
uint32_t cal_data_version;
err = nvs_get_u32(handle, PHY_CAL_VERSION_KEY, &cal_data_version);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: failed to get cal_version (%d)", __func__, err);
return err;
}
uint32_t cal_format_version = phy_get_rf_cal_version() & (~BIT(16));
ESP_LOGV(TAG, "phy_get_rf_cal_version: %d\n", cal_format_version);
if (cal_data_version != cal_format_version) {
ESP_LOGD(TAG, "%s: expected calibration data format %d, found %d",
__func__, cal_format_version, cal_data_version);
return ESP_FAIL;
}
uint8_t cal_data_mac[6];
size_t length = sizeof(cal_data_mac);
err = nvs_get_blob(handle, PHY_CAL_MAC_KEY, cal_data_mac, &length);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: failed to get cal_mac (%d)", __func__, err);
return err;
}
if (length != sizeof(cal_data_mac)) {
ESP_LOGD(TAG, "%s: invalid length of cal_mac (%d)", __func__, length);
return ESP_ERR_INVALID_SIZE;
}
uint8_t sta_mac[6];
system_efuse_read_mac(sta_mac);
if (memcmp(sta_mac, cal_data_mac, sizeof(sta_mac)) != 0) {
ESP_LOGE(TAG, "%s: calibration data MAC check failed: expected " \
MACSTR ", found " MACSTR,
__func__, MAC2STR(sta_mac), MAC2STR(cal_data_mac));
return ESP_FAIL;
}
length = sizeof(*out_cal_data);
err = nvs_get_blob(handle, PHY_CAL_DATA_KEY, out_cal_data, &length);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: failed to get cal_data(%d)", __func__, err);
return err;
}
if (length != sizeof(*out_cal_data)) {
ESP_LOGD(TAG, "%s: invalid length of cal_data (%d)", __func__, length);
return ESP_ERR_INVALID_SIZE;
}
return ESP_OK;
}
static esp_err_t store_cal_data_to_nvs_handle(nvs_handle handle,
const esp_phy_calibration_data_t* cal_data)
{
esp_err_t err;
uint32_t cal_format_version = phy_get_rf_cal_version() & (~BIT(16));
ESP_LOGV(TAG, "phy_get_rf_cal_version: %d\n", cal_format_version);
err = nvs_set_u32(handle, PHY_CAL_VERSION_KEY, cal_format_version);
if (err != ESP_OK) {
return err;
}
uint8_t sta_mac[6];
system_efuse_read_mac(sta_mac);
err = nvs_set_blob(handle, PHY_CAL_MAC_KEY, sta_mac, sizeof(sta_mac));
if (err != ESP_OK) {
return err;
}
err = nvs_set_blob(handle, PHY_CAL_DATA_KEY, cal_data, sizeof(*cal_data));
return err;
}
void register_chipv7_phy_stub()
{
}

View File

@ -0,0 +1,139 @@
// Copyright 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.
#pragma once
#include "esp_phy_init.h"
#include "sdkconfig.h"
// constrain a value between 'low' and 'high', inclusive
#define LIMIT(val, low, high) ((val < low) ? low : (val > high) ? high : val)
#define PHY_INIT_MAGIC "PHYINIT"
static const char phy_init_magic_pre[] = PHY_INIT_MAGIC;
/**
* @brief Structure containing default recommended PHY initialization parameters.
*/
static const esp_phy_init_data_t phy_init_data= {
.param_ver_id = 0,
.crystal_select = 3,
.wifi_rx_gain_swp_step_1 = 0x05,
.wifi_rx_gain_swp_step_2 = 0x04,
.wifi_rx_gain_swp_step_3 = 0x06,
.wifi_rx_gain_swp_step_4 = 0x05,
.wifi_rx_gain_swp_step_5 = 0x01,
.wifi_rx_gain_swp_step_6 = 0x06,
.wifi_rx_gain_swp_step_7 = 0x05,
.wifi_rx_gain_swp_step_8 = 0x04,
.wifi_rx_gain_swp_step_9 = 0x06,
.wifi_rx_gain_swp_step_10 = 0x04,
.wifi_rx_gain_swp_step_11 = 0x05,
.wifi_rx_gain_swp_step_12 = 0x00,
.wifi_rx_gain_swp_step_13 = 0x00,
.wifi_rx_gain_swp_step_14 = 0x00,
.wifi_rx_gain_swp_step_15 = 0x00,
.bt_rx_gain_swp_step_1 = 0x05,
.bt_rx_gain_swp_step_2 = 0x04,
.bt_rx_gain_swp_step_3 = 0x06,
.bt_rx_gain_swp_step_4 = 0x05,
.bt_rx_gain_swp_step_5 = 0x01,
.bt_rx_gain_swp_step_6 = 0x06,
.bt_rx_gain_swp_step_7 = 0x05,
.bt_rx_gain_swp_step_8 = 0x00,
.bt_rx_gain_swp_step_9 = 0x00,
.bt_rx_gain_swp_step_10 = 0x00,
.bt_rx_gain_swp_step_11 = 0x00,
.bt_rx_gain_swp_step_12 = 0x00,
.bt_rx_gain_swp_step_13 = 0x00,
.bt_rx_gain_swp_step_14 = 0x00,
.bt_rx_gain_swp_step_15 = 0x00,
.gain_cmp_1 = 0x0a,
.gain_cmp_6 = 0x0a,
.gain_cmp_11 = 0x0c,
.gain_cmp_ext2_1 = 0xf0,
.gain_cmp_ext2_6 = 0xf0,
.gain_cmp_ext2_11 = 0xf0,
.gain_cmp_ext3_1 = 0xe0,
.gain_cmp_ext3_6 = 0xe0,
.gain_cmp_ext3_11 = 0xe0,
.gain_cmp_bt_ofs_1 = 0x18,
.gain_cmp_bt_ofs_6 = 0x18,
.gain_cmp_bt_ofs_11 = 0x18,
.target_power_qdb_0 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 78),
.target_power_qdb_1 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 76),
.target_power_qdb_2 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 74),
.target_power_qdb_3 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 68),
.target_power_qdb_4 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 64),
.target_power_qdb_5 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 52),
.target_power_index_mcs0 = 0,
.target_power_index_mcs1 = 0,
.target_power_index_mcs2 = 1,
.target_power_index_mcs3 = 1,
.target_power_index_mcs4 = 2,
.target_power_index_mcs5 = 3,
.target_power_index_mcs6 = 4,
.target_power_index_mcs7 = 5,
.pwr_ind_11b_en = 0,
.pwr_ind_11b_0 = 0,
.pwr_ind_11b_1 = 0,
.chan_backoff_en = 0,
.chan1_power_backoff_qdb = 0,
.chan2_power_backoff_qdb = 0,
.chan3_power_backoff_qdb = 0,
.chan4_power_backoff_qdb = 0,
.chan5_power_backoff_qdb = 0,
.chan6_power_backoff_qdb = 0,
.chan7_power_backoff_qdb = 0,
.chan8_power_backoff_qdb = 0,
.chan9_power_backoff_qdb = 0,
.chan10_power_backoff_qdb = 0,
.chan11_power_backoff_qdb = 0,
.chan12_power_backoff_qdb = 0,
.chan13_power_backoff_qdb = 0,
.chan14_power_backoff_qdb = 0,
.chan1_rate_backoff_index = 0,
.chan2_rate_backoff_index = 0,
.chan3_rate_backoff_index = 0,
.chan4_rate_backoff_index = 0,
.chan5_rate_backoff_index = 0,
.chan6_rate_backoff_index = 0,
.chan7_rate_backoff_index = 0,
.chan8_rate_backoff_index = 0,
.chan9_rate_backoff_index = 0,
.chan10_rate_backoff_index = 0,
.chan11_rate_backoff_index = 0,
.chan12_rate_backoff_index = 0,
.chan13_rate_backoff_index = 0,
.chan14_rate_backoff_index = 0,
.spur_freq_cfg_msb_1 = 0,
.spur_freq_cfg_1 = 0,
.spur_freq_cfg_div_1 = 0,
.spur_freq_en_h_1 = 0,
.spur_freq_en_l_1 = 0,
.spur_freq_cfg_msb_2 = 0,
.spur_freq_cfg_2 = 0,
.spur_freq_cfg_div_2 = 0,
.spur_freq_en_h_2 = 0,
.spur_freq_en_l_2 = 0,
.spur_freq_cfg_msb_3 = 0,
.spur_freq_cfg_3 = 0,
.spur_freq_cfg_div_3 = 0,
.spur_freq_en_h_3 = 0,
.spur_freq_en_l_3 = 0,
.reserved = {0}
};
static const char phy_init_magic_post[] = PHY_INIT_MAGIC;

View File

@ -18,27 +18,13 @@
extern "C" { extern "C" {
#endif #endif
/** Initialise NVS flash storage with default flash sector layout /**
* @brief Initialize NVS flash storage with layout given in the partition table.
Temporarily, this region is hardcoded as a 12KB (0x3000 byte) *
region starting at 36KB (0x9000 byte) offset in flash. * @return ESP_OK if storage was successfully initialized.
*/
@return ESP_OK if flash was successfully initialised.
*/
esp_err_t nvs_flash_init(void); esp_err_t nvs_flash_init(void);
/** Initialise NVS flash storage with custom flash sector layout
@param baseSector Flash sector (units of 4096 bytes) offset to start NVS.
@param sectorCount Length (in flash sectors) of NVS region.
@return ESP_OK if flash was successfully initialised.
@note Use this parameter if you're not using the options in menuconfig for
configuring flash layout & partition table.
*/
esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -16,6 +16,7 @@
#include "nvs_storage.hpp" #include "nvs_storage.hpp"
#include "intrusive_list.h" #include "intrusive_list.h"
#include "nvs_platform.hpp" #include "nvs_platform.hpp"
#include "esp_partition.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
@ -61,20 +62,32 @@ extern "C" void nvs_dump()
s_nvs_storage.debugDump(); s_nvs_storage.debugDump();
} }
extern "C" esp_err_t nvs_flash_init(void)
{
return nvs_flash_init_custom(9, 3);
}
extern "C" esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount) extern "C" esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount)
{ {
Lock::init(); ESP_LOGD(TAG, "nvs_flash_init_custom start=%d count=%d", baseSector, sectorCount);
Lock lock;
ESP_LOGD(TAG, "init start=%d count=%d", baseSector, sectorCount);
s_nvs_handles.clear(); s_nvs_handles.clear();
return s_nvs_storage.init(baseSector, sectorCount); return s_nvs_storage.init(baseSector, sectorCount);
} }
#ifdef ESP_PLATFORM
extern "C" esp_err_t nvs_flash_init(void)
{
Lock::init();
Lock lock;
if (s_nvs_storage.isValid()) {
return ESP_OK;
}
const esp_partition_t* partition = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
if (partition == NULL) {
return ESP_ERR_NOT_FOUND;
}
return nvs_flash_init_custom(partition->address / SPI_FLASH_SEC_SIZE,
partition->size / SPI_FLASH_SEC_SIZE);
}
#endif
static esp_err_t nvs_find_ns_handle(nvs_handle handle, HandleEntry& entry) static esp_err_t nvs_find_ns_handle(nvs_handle handle, HandleEntry& entry)
{ {
auto it = find_if(begin(s_nvs_handles), end(s_nvs_handles), [=](HandleEntry& e) -> bool { auto it = find_if(begin(s_nvs_handles), end(s_nvs_handles), [=](HandleEntry& e) -> bool {

View File

@ -114,7 +114,30 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size)
assert(mFirstUsedEntry != INVALID_ENTRY); assert(mFirstUsedEntry != INVALID_ENTRY);
const uint16_t count = size / ENTRY_SIZE; const uint16_t count = size / ENTRY_SIZE;
auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), data, size); const uint8_t* buf = data;
#ifdef ESP_PLATFORM
/* On the ESP32, data can come from DROM, which is not accessible by spi_flash_write
* function. To work around this, we copy the data to heap if it came from DROM.
* Hopefully this won't happen very often in practice. For data from DRAM, we should
* still be able to write it to flash directly.
* TODO: figure out how to make this platform-specific check nicer (probably by introducing
* a platform-specific flash layer).
*/
if ((uint32_t) data < 0x3ff00000) {
buf = (uint8_t*) malloc(size);
if (!buf) {
return ESP_ERR_NO_MEM;
}
memcpy((void*)buf, data, size);
}
#endif //ESP_PLATFORM
auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), buf, size);
#ifdef ESP_PLATFORM
if (buf != data) {
free((void*)buf);
}
#endif //ESP_PLATFORM
if (rc != ESP_OK) { if (rc != ESP_OK) {
mState = PageState::INVALID; mState = PageState::INVALID;
return rc; return rc;

View File

@ -16,9 +16,6 @@
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#define NVS_DEBUGV(...) ets_printf(__VA_ARGS__)
#include "rom/ets_sys.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
@ -30,19 +27,23 @@ class Lock
public: public:
Lock() Lock()
{ {
assert(mSemaphore); if (mSemaphore) {
xSemaphoreTake(mSemaphore, portMAX_DELAY); xSemaphoreTake(mSemaphore, portMAX_DELAY);
}
} }
~Lock() ~Lock()
{ {
assert(mSemaphore); if (mSemaphore) {
xSemaphoreGive(mSemaphore); xSemaphoreGive(mSemaphore);
}
} }
static esp_err_t init() static esp_err_t init()
{ {
assert(mSemaphore == nullptr); if (mSemaphore) {
return ESP_OK;
}
mSemaphore = xSemaphoreCreateMutex(); mSemaphore = xSemaphoreCreateMutex();
if (!mSemaphore) { if (!mSemaphore) {
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
@ -52,7 +53,9 @@ public:
static void uninit() static void uninit()
{ {
vSemaphoreDelete(mSemaphore); if (mSemaphore) {
vSemaphoreDelete(mSemaphore);
}
mSemaphore = nullptr; mSemaphore = nullptr;
} }

View File

@ -69,6 +69,11 @@ esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount)
return ESP_OK; return ESP_OK;
} }
bool Storage::isValid() const
{
return mState == StorageState::ACTIVE;
}
esp_err_t Storage::findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item) esp_err_t Storage::findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item)
{ {
for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) {

View File

@ -47,6 +47,8 @@ public:
esp_err_t init(uint32_t baseSector, uint32_t sectorCount); esp_err_t init(uint32_t baseSector, uint32_t sectorCount);
bool isValid() const;
esp_err_t createOrOpenNamespace(const char* nsName, bool canCreate, uint8_t& nsIndex); esp_err_t createOrOpenNamespace(const char* nsName, bool canCreate, uint8_t& nsIndex);
esp_err_t writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize); esp_err_t writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize);

View File

@ -0,0 +1,47 @@
// 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "nvs_flash.h"
/**
* @brief Initialize NVS flash storage with custom flash sector layout
*
* @note This API is intended to be used in unit tests.
*
* @param baseSector Flash sector (units of 4096 bytes) offset to start NVS
* @param sectorCount Length (in flash sectors) of NVS region.
NVS partition must be at least 3 sectors long.
* @return ESP_OK if flash was successfully initialized
*/
esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount);
/**
* @brief Dump contents of NVS storage to stdout
*
* This function may be used for debugging purposes to inspect the state
* of NVS pages. For each page, list of entries is also dumped.
*/
void nvs_dump(void);
#ifdef __cplusplus
}
#endif

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
#include "catch.hpp" #include "catch.hpp"
#include "nvs.hpp" #include "nvs.hpp"
#include "nvs_flash.h" #include "nvs_test_api.h"
#include "spi_flash_emulation.h" #include "spi_flash_emulation.h"
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>

View File

@ -1,49 +1,63 @@
menu "Partition Table" menu "Partition Table"
choice choice
prompt "Partition Table" prompt "Partition Table"
default PARTITION_TABLE_SINGLE_APP default PARTITION_TABLE_SINGLE_APP
help help
The partition table to flash to the ESP32. The partition table The partition table to flash to the ESP32. The partition table
determines where apps, data and other resources are expected to determines where apps, data and other resources are expected to
be found. be found.
The predefined partition table CSV descriptions can be found The predefined partition table CSV descriptions can be found
in the components/partition_table directory. Otherwise it's in the components/partition_table directory. Otherwise it's
possible to create a new custom partition CSV for your application. possible to create a new custom partition CSV for your application.
config PARTITION_TABLE_SINGLE_APP config PARTITION_TABLE_SINGLE_APP
bool "Single factory app, no OTA" bool "Single factory app, no OTA"
config PARTITION_TABLE_TWO_OTA config PARTITION_TABLE_TWO_OTA
bool "Factory app, two OTA definitions" bool "Factory app, two OTA definitions"
config PARTITION_TABLE_CUSTOM config PARTITION_TABLE_CUSTOM
bool "Custom partition table CSV" bool "Custom partition table CSV"
endchoice endchoice
config PARTITION_TABLE_CUSTOM_FILENAME config PARTITION_TABLE_CUSTOM_FILENAME
string "Custom partition CSV file" if PARTITION_TABLE_CUSTOM string "Custom partition CSV file" if PARTITION_TABLE_CUSTOM
default partitions.csv default partitions.csv
help help
Name of the custom partition CSV filename. This path is evaluated Name of the custom partition CSV filename. This path is evaluated
relative to the project root directory. relative to the project root directory.
config PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET config PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET
hex "App offset in flash" if PARTITION_TABLE_CUSTOM hex "Factory app partition offset" if PARTITION_TABLE_CUSTOM
default 0x10000 default 0x10000
help help
If using a custom partition table, specify the offset in the flash If using a custom partition table, specify the offset in the flash
where 'make flash' should write the built app. where 'make flash' should write the built app.
config PARTITION_TABLE_CUSTOM_PHY_DATA_OFFSET
hex "PHY data partition offset" if PARTITION_TABLE_CUSTOM
depends on ESP32_PHY_INIT_DATA_IN_PARTITION
default 0xf000
help
If using a custom partition table, specify the offset in the flash
where 'make flash' should write the initial PHY data file.
config PARTITION_TABLE_FILENAME config PARTITION_TABLE_FILENAME
string string
default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP
default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA
default PARTITION_TABLE_CUSTOM_FILENAME if PARTITION_TABLE_CUSTOM default PARTITION_TABLE_CUSTOM_FILENAME if PARTITION_TABLE_CUSTOM
config APP_OFFSET config APP_OFFSET
hex hex
default PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET if PARTITION_TABLE_CUSTOM default PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET if PARTITION_TABLE_CUSTOM
default 0x10000 # this is the factory app offset used by the default tables default 0x10000 # this is the factory app offset used by the default tables
config PHY_DATA_OFFSET
hex
default PARTITION_TABLE_CUSTOM_PHY_DATA_OFFSET if PARTITION_TABLE_CUSTOM
default 0xf000 # this is the factory app offset used by the default tables
endmenu endmenu

View File

@ -117,6 +117,7 @@ class PartitionDefinition(object):
"data" : DATA_TYPE, "data" : DATA_TYPE,
} }
# Keep this map in sync with esp_partition_subtype_t enum in esp_partition.h
SUBTYPES = { SUBTYPES = {
APP_TYPE : { APP_TYPE : {
"factory" : 0x00, "factory" : 0x00,
@ -124,8 +125,11 @@ class PartitionDefinition(object):
}, },
DATA_TYPE : { DATA_TYPE : {
"ota" : 0x00, "ota" : 0x00,
"rf" : 0x01, "phy" : 0x01,
"wifi" : 0x02, "nvs" : 0x02,
"esphttpd" : 0x80,
"fat" : 0x81,
"spiffs" : 0x82,
}, },
} }

View File

@ -1,4 +1,5 @@
# Name, Type, SubType, Offset, Size # Name, Type, SubType, Offset, Size
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
nvs, data, nvs, 0x9000, 0x6000
phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 1M factory, app, factory, 0x10000, 1M
rfdata, data, rf, , 256K
wifidata, data, wifi, , 256K

1 # Name # Name, Type, SubType, Offset, Size Type SubType Offset Size
2 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
3 nvs, data, nvs, 0x9000, 0x6000
4 phy_init, data, phy, 0xf000, 0x1000
5 factory factory, app, factory, 0x10000, 1M app factory 0x10000 1M
rfdata data rf 256K
wifidata data wifi 256K

View File

@ -1,7 +1,8 @@
# Name, Type, SubType, Offset, Size # Name, Type, SubType, Offset, Size
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
nvs, data, nvs, 0x9000, 0x4000
otadata, data, ota, 0xd000, 0x2000
phy_init, data, phy, 0xf000, 0x1000
factory, 0, 0, 0x10000, 1M factory, 0, 0, 0x10000, 1M
ota_0, 0, ota_0, , 1M ota_0, 0, ota_0, , 1M
ota_1, 0, ota_1, , 1M ota_1, 0, ota_1, , 1M
rfdata, data, rf, , 256K
wifidata, data, wifi, , 256K
otadata, data, ota, , 256K

1 # Name # Name, Type, SubType, Offset, Size Type SubType Offset Size
2 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
3 nvs, data, nvs, 0x9000, 0x4000
4 otadata, data, ota, 0xd000, 0x2000
5 phy_init, data, phy, 0xf000, 0x1000
6 factory factory, 0, 0, 0x10000, 1M 0 0 0x10000 1M
7 ota_0 ota_0, 0, ota_0, , 1M 0 ota_0 1M
8 ota_1 ota_1, 0, ota_1, , 1M 0 ota_1 1M
rfdata data rf 256K
wifidata data wifi 256K
otadata data ota 256K

View File

@ -135,6 +135,12 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const void *src, size_t si
if (size % 4 != 0) { if (size % 4 != 0) {
return ESP_ERR_INVALID_SIZE; return ESP_ERR_INVALID_SIZE;
} }
if ((uint32_t) src < 0x3ff00000) {
// if source address is in DROM, we won't be able to read it
// from within SPIWrite
// TODO: consider buffering source data using heap and writing it anyway?
return ESP_ERR_INVALID_ARG;
}
// Out of bound writes are checked in ROM code, but we can give better // Out of bound writes are checked in ROM code, but we can give better
// error code here // error code here
if (dest_addr + size > g_rom_flashchip.chip_size) { if (dest_addr + size > g_rom_flashchip.chip_size) {

View File

@ -76,6 +76,8 @@ esp_err_t spi_flash_erase_range(size_t start_address, size_t size);
* *
* @note Address in flash, dest, has to be 4-byte aligned. * @note Address in flash, dest, has to be 4-byte aligned.
* This is a temporary limitation which will be removed. * This is a temporary limitation which will be removed.
* @note If source address is in DROM, this function will return
* ESP_ERR_INVALID_ARG.
* *
* @param dest destination address in Flash * @param dest destination address in Flash
* @param src pointer to the source buffer * @param src pointer to the source buffer

View File

@ -17,40 +17,30 @@ The simplest way to use the partition table is to `make menuconfig` and choose o
In both cases the factory app is flashed at offset 0x10000. If you `make partition_table` then it will print a summary of the partition table. In both cases the factory app is flashed at offset 0x10000. If you `make partition_table` then it will print a summary of the partition table.
Known Issues
------------
The below design document outlines the goals for the partition table system. At the moment, only some features are used:
- data partition types "rf" & "wifi" are unused and can be entirely omitted to save space.
- NVS (non-volatile-storage) uses a hardcoded 12KB (0x3000 byte) region at offset 0x9000.
Once a full user API is in place for partition access, these limitations will be resolved and you'll be able to use the partition mechanism fully for storing data in flash.
Built-in Partition Tables Built-in Partition Tables
------------------------- -------------------------
Here is the summary printed for the "Single factory app, no OTA" configuration:: Here is the summary printed for the "Single factory app, no OTA" configuration::
# Espressif ESP32 Partition Table # Espressif ESP32 Partition Table
# Name, Type, SubType, Offset, Size # Name, Type, SubType, Offset, Size
factory, app, factory, 0x10000, 1M nvs, data, nvs, 0x9000, 0x6000
rfdata, data, rf, 0x110000, 256K phy_init, data, phy, 0xf000, 0x1000
wifidata,data, wifi, 0x150000, 256K factory, app, factory, 0x10000, 1M
* At a 0x10000 (64KB) offset in the flash is the app labelled "factory". The bootloader will run this app by default. * At a 0x10000 (64KB) offset in the flash is the app labelled "factory". The bootloader will run this app by default.
* There are also two data regions defined in the partition table for storing RF & Wifi calibration data. * There are also two data regions defined in the partition table for storing NVS library partition and PHY init data.
Here is the summary printed for the "Factory app, two OTA definitions" configuration:: Here is the summary printed for the "Factory app, two OTA definitions" configuration::
# Espressif ESP32 Partition Table # Espressif ESP32 Partition Table
# Name, Type, SubType, Offset, Size # Name, Type, SubType, Offset, Size
factory, app, factory, 0x10000, 1M nvs, data, nvs, 0x9000, 0x4000
ota_0, app, ota_0, 0x110000, 1M otadata, data, ota, 0xd000, 0x2000
ota_1, app, ota_1, 0x210000, 1M phy_init, data, phy, 0xf000, 0x1000
rfdata, data, rf, 0x310000, 256K factory, 0, 0, 0x10000, 1M
wifidata,data, wifi, 0x350000, 256K ota_0, 0, ota_0, , 1M
otadata, data, ota, 0x390000, 256K ota_1, 0, ota_1, , 1M
* There are now three app partition definitions. * There are now three app partition definitions.
* The type of all three are set as "app", but the subtype varies between the factory app at 0x10000 and the next two "OTA" apps. * The type of all three are set as "app", but the subtype varies between the factory app at 0x10000 and the next two "OTA" apps.
@ -65,12 +55,12 @@ If you choose "Custom partition table CSV" in menuconfig then you can also enter
The CSV format is the same format as printed in the summaries shown above. However, not all fields are required in the CSV. For example, here is the "input" CSV for the OTA partition table:: The CSV format is the same format as printed in the summaries shown above. However, not all fields are required in the CSV. For example, here is the "input" CSV for the OTA partition table::
# Name, Type, SubType, Offset, Size # Name, Type, SubType, Offset, Size
nvs, data, nvs, 0x9000, 0x4000
otadata, data, ota, 0xd000, 0x2000
phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 1M factory, app, factory, 0x10000, 1M
ota_0, app, ota_0, , 1M ota_0, app, ota_0, , 1M
ota_1, app, ota_1, , 1M ota_1, app, ota_1, , 1M
rfdata, data, rf, , 256K
wifidata, data, wifi, , 256K
otadata, data, ota, , 256K
* Whitespace between fields is ignored, and so is any line starting with # (comments). * Whitespace between fields is ignored, and so is any line starting with # (comments).
* Each non-comment line in the CSV file is a partition definition. * Each non-comment line in the CSV file is a partition definition.
@ -93,7 +83,7 @@ Subtype
When type is "app", the subtype field can be specified as factory (0), ota_0 (0x10) ... ota_15 (0x1F) and test (0x20). Or it can be any number 0-255 (0x00-0xFF). The bootloader will execute the factory app unless there it sees a partition of type data/ota, in which case it reads this partition to determine which OTA image to boot When type is "app", the subtype field can be specified as factory (0), ota_0 (0x10) ... ota_15 (0x1F) and test (0x20). Or it can be any number 0-255 (0x00-0xFF). The bootloader will execute the factory app unless there it sees a partition of type data/ota, in which case it reads this partition to determine which OTA image to boot
When type is "data", the subtype field can be specified as ota (0), rf (1), wifi (2). Or it can be a number 0x00-0xFF. The bootloader ignores all data subtypes except for ota. Other "data" subtypes are reserved for Espressif use. To create custom data partition subtypes then use a custom type value, and choose any subtype 0x00-0xFF. When type is "data", the subtype field can be specified as ota (0), phy (1), nvs (2). Or it can be a number 0x00-0xFF. The bootloader ignores all data subtypes except for ota. Subtypes 0-0x7f are reserved for Espressif use. To create custom data partition subtypes use "data" type, and choose any unused subtype in 0x80-0xFF range. If you are porting a filesystem to the ESP-IDF, consider opening a PR to add the new subtype to esp_partition.h file.
Offset & Size Offset & Size
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
@ -104,6 +94,8 @@ App partitions have to be at offsets aligned to 0x10000 (64K). If you leave the
Sizes and offsets can be specified as decimal numbers, hex numbers with the prefix 0x, or size multipliers M or K (1024 and 1024*1024 bytes). Sizes and offsets can be specified as decimal numbers, hex numbers with the prefix 0x, or size multipliers M or K (1024 and 1024*1024 bytes).
NVS data partition has to be at least 0x3000 bytes long, and OTA data parition has to be 0x2000 bytes long. If you are using NVS in your application to store a lot of data, consider using a custom partition table with larger NVS partition.
Generating Binary Partition Table Generating Binary Partition Table
--------------------------------- ---------------------------------