efuse: Add the batch writing mode

This mode should be used when burning several efuses at one time.
This commit is contained in:
KonstantinKondrashov 2019-09-18 21:19:48 +08:00
parent d7fa288a6c
commit cf762d91c2
4 changed files with 226 additions and 27 deletions

View file

@ -327,6 +327,59 @@ esp_err_t esp_efuse_update_secure_version(uint32_t secure_version);
*/
void esp_efuse_init(uint32_t offset, uint32_t size);
/* @brief Set the batch mode of writing fields.
*
* This mode allows you to write the fields in the batch mode.
* If this mode is enabled, esp_efuse_batch_write_commit() must be called
* to actually burn any written efuses.
* In this mode, reading efuse is not possible.
* This mode should be used when burning several efuses at one time.
*
* \code{c}
* // Example of using the batch writing mode.
*
* // set the batch writing mode
* esp_efuse_batch_write_begin();
*
* // use any writing functions as usual
* esp_efuse_write_field_blob(ESP_EFUSE_...);
* esp_efuse_write_field_cnt(ESP_EFUSE_...);
* esp_efuse_set_write_protect(EFUSE_BLKx);
* esp_efuse_write_reg(EFUSE_BLKx, ...);
* esp_efuse_write_block(EFUSE_BLKx, ...);
* ...
*
* // Write all of these fields to the efuse registers
* esp_efuse_batch_write_commit();
*
* \endcode
*
* @return
* - ESP_OK: Successful.
*/
esp_err_t esp_efuse_batch_write_begin(void);
/* @brief Reset the batch mode of writing fields.
*
* It will reset the batch writing mode and any written changes.
*
* @return
* - ESP_OK: Successful.
* - ESP_ERR_INVALID_STATE: Tha batch mode was not set.
*/
esp_err_t esp_efuse_batch_write_cancel(void);
/* @brief Writes all prepared data for the batch mode.
*
* Must be called to ensure changes are written to the efuse registers.
* After this the batch writing mode will be reset.
*
* @return
* - ESP_OK: Successful.
* - ESP_ERR_INVALID_STATE: The deferred writing mode was not set.
*/
esp_err_t esp_efuse_batch_write_commit(void);
#ifdef __cplusplus
}
#endif

View file

@ -24,13 +24,19 @@ const static char *TAG = "efuse";
#if defined(BOOTLOADER_BUILD)
#define EFUSE_LOCK_ACQUIRE()
#define EFUSE_LOCK_RELEASE()
#define EFUSE_LOCK_ACQUIRE_RUCURSIVE()
#define EFUSE_LOCK_RELEASE_RUCURSIVE()
#else
#include <sys/lock.h>
static _lock_t s_efuse_lock;
#define EFUSE_LOCK_ACQUIRE() _lock_acquire(&s_efuse_lock)
#define EFUSE_LOCK_RELEASE() _lock_release(&s_efuse_lock)
#define EFUSE_LOCK_ACQUIRE_RUCURSIVE() _lock_acquire_recursive(&s_efuse_lock)
#define EFUSE_LOCK_RELEASE_RUCURSIVE() _lock_release_recursive(&s_efuse_lock)
#endif
static bool s_batch_writing_mode = false;
// Public API functions
// read value from EFUSE, writing it into an array
@ -66,51 +72,62 @@ esp_err_t esp_efuse_read_field_cnt(const esp_efuse_desc_t* field[], size_t* out_
// write array to EFUSE
esp_err_t esp_efuse_write_field_blob(const esp_efuse_desc_t* field[], const void* src, size_t src_size_bits)
{
EFUSE_LOCK_ACQUIRE();
EFUSE_LOCK_ACQUIRE_RUCURSIVE();
esp_err_t err = ESP_OK;
if (field == NULL || src == NULL || src_size_bits == 0) {
err = ESP_ERR_INVALID_ARG;
} else {
esp_efuse_utility_reset();
if (s_batch_writing_mode == false) {
esp_efuse_utility_reset();
}
err = esp_efuse_utility_process(field, (void*)src, src_size_bits, esp_efuse_utility_write_blob);
if (err == ESP_OK) {
err = esp_efuse_utility_apply_new_coding_scheme();
if (s_batch_writing_mode == false) {
if (err == ESP_OK) {
esp_efuse_utility_burn_efuses();
err = esp_efuse_utility_apply_new_coding_scheme();
if (err == ESP_OK) {
esp_efuse_utility_burn_efuses();
}
}
esp_efuse_utility_reset();
}
esp_efuse_utility_reset();
}
EFUSE_LOCK_RELEASE();
EFUSE_LOCK_RELEASE_RUCURSIVE();
return err;
}
// program cnt bits to "1"
esp_err_t esp_efuse_write_field_cnt(const esp_efuse_desc_t* field[], size_t cnt)
{
EFUSE_LOCK_ACQUIRE();
EFUSE_LOCK_ACQUIRE_RUCURSIVE();
esp_err_t err = ESP_OK;
if (field == NULL || cnt == 0) {
err = ESP_ERR_INVALID_ARG;
} else {
esp_efuse_utility_reset();
if (s_batch_writing_mode == false) {
esp_efuse_utility_reset();
}
err = esp_efuse_utility_process(field, &cnt, 0, esp_efuse_utility_write_cnt);
if (cnt != 0) {
ESP_LOGE(TAG, "The required number of bits can not be set. [Not set %d]", cnt);
err = ESP_ERR_EFUSE_CNT_IS_FULL;
}
if (err == ESP_OK_EFUSE_CNT || err == ESP_OK) {
err = esp_efuse_utility_apply_new_coding_scheme();
if (err == ESP_OK) {
esp_efuse_utility_burn_efuses();
}
if (err == ESP_OK_EFUSE_CNT) {
err = ESP_OK;
}
if (s_batch_writing_mode == false) {
if (err == ESP_OK) {
err = esp_efuse_utility_apply_new_coding_scheme();
if (err == ESP_OK) {
esp_efuse_utility_burn_efuses();
}
}
esp_efuse_utility_reset();
}
esp_efuse_utility_reset();
}
EFUSE_LOCK_RELEASE();
EFUSE_LOCK_RELEASE_RUCURSIVE();
return err;
}
@ -140,17 +157,21 @@ uint32_t esp_efuse_read_reg(esp_efuse_block_t blk, unsigned int num_reg)
// writing efuse register.
esp_err_t esp_efuse_write_reg(esp_efuse_block_t blk, unsigned int num_reg, uint32_t val)
{
EFUSE_LOCK_ACQUIRE();
esp_efuse_utility_reset();
esp_err_t err = esp_efuse_utility_write_reg(blk, num_reg, val);
if (err == ESP_OK) {
err = esp_efuse_utility_apply_new_coding_scheme();
if (err == ESP_OK) {
esp_efuse_utility_burn_efuses();
}
EFUSE_LOCK_ACQUIRE_RUCURSIVE();
if (s_batch_writing_mode == false) {
esp_efuse_utility_reset();
}
esp_efuse_utility_reset();
EFUSE_LOCK_RELEASE();
esp_err_t err = esp_efuse_utility_write_reg(blk, num_reg, val);
if (s_batch_writing_mode == false) {
if (err == ESP_OK) {
err = esp_efuse_utility_apply_new_coding_scheme();
if (err == ESP_OK) {
esp_efuse_utility_burn_efuses();
}
}
esp_efuse_utility_reset();
}
EFUSE_LOCK_RELEASE_RUCURSIVE();
return err;
}
@ -193,3 +214,39 @@ esp_err_t esp_efuse_write_block(esp_efuse_block_t blk, const void* src_key, size
}
return err;
}
esp_err_t esp_efuse_batch_write_begin(void)
{
EFUSE_LOCK_ACQUIRE();
s_batch_writing_mode = true;
esp_efuse_utility_reset();
ESP_LOGI(TAG, "Batch mode of writing fields is enabled");
return ESP_OK;
}
esp_err_t esp_efuse_batch_write_cancel(void)
{
if (s_batch_writing_mode == true) {
s_batch_writing_mode = false;
esp_efuse_utility_reset();
ESP_LOGI(TAG, "Batch mode of writing fields is disabled");
EFUSE_LOCK_RELEASE();
return ESP_OK;
} else {
return ESP_ERR_INVALID_STATE;
}
}
esp_err_t esp_efuse_batch_write_commit(void)
{
if (s_batch_writing_mode == false) {
return ESP_ERR_INVALID_STATE;
} else {
esp_err_t err = esp_efuse_utility_apply_new_coding_scheme();
if (err == ESP_OK) {
esp_efuse_utility_burn_efuses();
}
esp_efuse_batch_write_cancel();
return err;
}
}

View file

@ -12,6 +12,10 @@
#include "esp_efuse_test_table.h"
#include "esp32/rom/efuse.h"
#include "bootloader_random.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "test_utils.h"
#include "sdkconfig.h"
static const char* TAG = "efuse_test";
@ -608,4 +612,82 @@ TEST_CASE("Test Bits are not empty. Write operation is forbidden", "[efuse]")
}
}
#ifndef CONFIG_FREERTOS_UNICORE
static const int delay_ms = 2000;
static xSemaphoreHandle sema;
static void task1(void* arg)
{
TEST_ESP_OK(esp_efuse_batch_write_begin());
ESP_LOGI(TAG, "Start work in batch mode");
xSemaphoreGive(sema);
vTaskDelay((delay_ms + 100) / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "Finish work in batch mode");
TEST_ESP_OK(esp_efuse_batch_write_cancel());
vTaskDelete(NULL);
}
static void task2(void* arg)
{
xSemaphoreTake(sema, portMAX_DELAY);
uint8_t mac[6];
int64_t t1 = esp_timer_get_time();
TEST_ESP_OK(esp_efuse_read_field_blob(ESP_EFUSE_MAC_FACTORY, &mac, sizeof(mac) * 8));
int64_t t2 = esp_timer_get_time();
int diff_ms = (t2 - t1) / 1000;
TEST_ASSERT_GREATER_THAN(delay_ms, diff_ms);
ESP_LOGI(TAG, "read MAC address: %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
xSemaphoreGive(sema);
vTaskDelete(NULL);
}
static void task3(void* arg)
{
xSemaphoreTake(sema, portMAX_DELAY);
size_t test3_len_6 = 2;
int64_t t1 = esp_timer_get_time();
TEST_ESP_OK(esp_efuse_write_field_cnt(ESP_EFUSE_TEST3_LEN_6, test3_len_6));
TEST_ESP_OK(esp_efuse_read_field_cnt(ESP_EFUSE_TEST3_LEN_6, &test3_len_6));
int64_t t2 = esp_timer_get_time();
ESP_LOGI(TAG, "write&read test3_len_6: %d", test3_len_6);
int diff_ms = (t2 - t1) / 1000;
TEST_ASSERT_GREATER_THAN(delay_ms, diff_ms);
TEST_ASSERT_EQUAL_INT(2, test3_len_6);
xSemaphoreGive(sema);
vTaskDelete(NULL);
}
TEST_CASE("Batch mode is thread-safe", "[efuse]")
{
// Batch mode blocks work with efuse on other tasks.
esp_efuse_utility_update_virt_blocks();
esp_efuse_utility_debug_dump_blocks();
sema = xSemaphoreCreateBinary();
printf("\n");
xTaskCreatePinnedToCore(task1, "task1", 2048, NULL, UNITY_FREERTOS_PRIORITY - 1, NULL, 0);
xTaskCreatePinnedToCore(task2, "task2", 2048, NULL, UNITY_FREERTOS_PRIORITY - 1, NULL, 1);
vTaskDelay(3000 / portTICK_PERIOD_MS);
xSemaphoreTake(sema, portMAX_DELAY);
esp_efuse_utility_reset();
esp_efuse_utility_erase_virt_blocks();
printf("\n");
xTaskCreatePinnedToCore(task1, "task1", 2048, NULL, UNITY_FREERTOS_PRIORITY - 1, NULL, 0);
xTaskCreatePinnedToCore(task3, "task3", 2048, NULL, UNITY_FREERTOS_PRIORITY - 1, NULL, 1);
vTaskDelay(3000 / portTICK_PERIOD_MS);
xSemaphoreTake(sema, portMAX_DELAY);
printf("\n");
vSemaphoreDelete(sema);
esp_efuse_utility_reset();
esp_efuse_utility_erase_virt_blocks();
}
#endif // #ifndef CONFIG_FREERTOS_UNICORE
#endif // #ifdef CONFIG_EFUSE_VIRTUAL

View file

@ -146,6 +146,10 @@ Also, 3/4 coding scheme imposes restrictions on writing bits belonging to one co
It turns out that only one field can be written into one coding unit. Repeated rewriting in one coding unit is prohibited. But if the record was made in advance or through a :cpp:func:`esp_efuse_write_block` function, then reading the fields belonging to one coding unit is possible.
In case ``3/4`` coding scheme, the writing process is divided into the coding units and we can not use the usual mode of writing some fields. We can prepare all the data for writing and burn it in one time. You can also use this mode for ``None`` coding scheme but it is not necessary. It is important for ``3/4`` coding scheme.
To write some fields in one time need to use ``the batch writing mode``. Firstly set this mode through :cpp:func:`esp_efuse_batch_write_begin` function then write some fields as usual use the ``esp_efuse_write_...`` functions. At the end to burn they, need to call the :cpp:func:`esp_efuse_batch_write_commit` function. It burns prepared data to the efuse blocks and disable the ``batch recording mode``.
``The batch writing mode`` blocks ``esp_efuse_read_...`` operations.
After changing the coding scheme, run ``efuse_common_table`` and ``efuse_custom_table`` commands to check the tables of the new coding scheme.
eFuse API
@ -163,6 +167,9 @@ Access to the fields is via a pointer to the description structure. API function
* :cpp:func:`esp_efuse_get_coding_scheme` - returns eFuse coding scheme for blocks.
* :cpp:func:`esp_efuse_read_block` - reads key to eFuse block starting at the offset and the required size.
* :cpp:func:`esp_efuse_write_block` - writes key to eFuse block starting at the offset and the required size.
* :cpp:func:`esp_efuse_batch_write_begin` - set the batch mode of writing fields.
* :cpp:func:`esp_efuse_batch_write_commit` - writes all prepared data for batch writing mode and reset the batch writing mode.
* :cpp:func:`esp_efuse_batch_write_cancel` - reset the batch writing mode and prepared data.
For frequently used fields, special functions are made, like this :cpp:func:`esp_efuse_get_chip_ver`, :cpp:func:`esp_efuse_get_pkg_ver`.