From 2ba9cd6c54a6596f25fa22734e63f6f325befa0b Mon Sep 17 00:00:00 2001 From: KonstantinKondrashov Date: Tue, 5 Apr 2022 23:02:22 +0800 Subject: [PATCH] efuse: Validates data after burning and re-burnes it if necessary It checks the content of the written data and encoding errors. --- .../efuse/private_include/esp_efuse_utility.h | 13 ++ .../efuse/src/esp32/esp_efuse_utility.c | 202 +++++++++++------- .../efuse/src/esp32c3/esp_efuse_utility.c | 65 +++++- .../efuse/src/esp32s2/esp_efuse_utility.c | 63 +++++- .../efuse/src/esp32s3/esp_efuse_utility.c | 63 +++++- components/efuse/src/esp_efuse_utility.c | 26 +++ 6 files changed, 350 insertions(+), 82 deletions(-) diff --git a/components/efuse/private_include/esp_efuse_utility.h b/components/efuse/private_include/esp_efuse_utility.h index 5345b0bbab..ada5d97bff 100644 --- a/components/efuse/private_include/esp_efuse_utility.h +++ b/components/efuse/private_include/esp_efuse_utility.h @@ -155,6 +155,19 @@ esp_err_t esp_efuse_utility_apply_new_coding_scheme(void); */ void esp_efuse_utility_clear_program_registers(void); +/** + * @brief Checks the correctness of burned data in the given block. + * + * @note Internal use. Do not call it. + * + * @param[in] block Index of efuse block. + * @param[in] r_data_len Block length for reading data in bytes (multiple of 4). + * + * @return True - written data are correct. + * False - written data are incorrect. + */ +bool esp_efuse_utility_is_correct_written_data(esp_efuse_block_t block, unsigned r_data_len); + #ifdef __cplusplus } #endif diff --git a/components/efuse/src/esp32/esp_efuse_utility.c b/components/efuse/src/esp32/esp_efuse_utility.c index 7d95f09cc0..52036a5635 100644 --- a/components/efuse/src/esp32/esp_efuse_utility.c +++ b/components/efuse/src/esp32/esp_efuse_utility.c @@ -34,12 +34,14 @@ const esp_efuse_range_addr_t range_read_addr_blocks[] = { {EFUSE_BLK3_RDATA0_REG, EFUSE_BLK3_RDATA7_REG} // range address of EFUSE_BLK3 }; -/*Range addresses to write blocks*/ +static uint32_t write_mass_blocks[EFUSE_BLK_MAX][COUNT_EFUSE_REG_PER_BLOCK] = { 0 }; + +/*Range addresses to write blocks (it is not real regs, it is a buffer) */ const esp_efuse_range_addr_t range_write_addr_blocks[] = { - {EFUSE_BLK0_WDATA0_REG, EFUSE_BLK0_WDATA6_REG}, // range address of EFUSE_BLK0 - {EFUSE_BLK1_WDATA0_REG, EFUSE_BLK1_WDATA7_REG}, // range address of EFUSE_BLK1 - {EFUSE_BLK2_WDATA0_REG, EFUSE_BLK2_WDATA7_REG}, // range address of EFUSE_BLK2 - {EFUSE_BLK3_WDATA0_REG, EFUSE_BLK3_WDATA7_REG} // range address of EFUSE_BLK3 + {(uint32_t) &write_mass_blocks[EFUSE_BLK0][0], (uint32_t) &write_mass_blocks[EFUSE_BLK0][6]}, + {(uint32_t) &write_mass_blocks[EFUSE_BLK1][0], (uint32_t) &write_mass_blocks[EFUSE_BLK1][7]}, + {(uint32_t) &write_mass_blocks[EFUSE_BLK2][0], (uint32_t) &write_mass_blocks[EFUSE_BLK2][7]}, + {(uint32_t) &write_mass_blocks[EFUSE_BLK3][0], (uint32_t) &write_mass_blocks[EFUSE_BLK3][7]}, }; #define EFUSE_CONF_WRITE 0x5A5A /* eFuse_pgm_op_ena, force no rd/wr disable. */ @@ -48,6 +50,16 @@ const esp_efuse_range_addr_t range_write_addr_blocks[] = { #define EFUSE_CMD_READ 0x01 /* Command to read. */ #ifndef CONFIG_EFUSE_VIRTUAL +/* Addresses to write blocks*/ +const uint32_t start_write_addr[] = { + EFUSE_BLK0_WDATA0_REG, + EFUSE_BLK1_WDATA0_REG, + EFUSE_BLK2_WDATA0_REG, + EFUSE_BLK3_WDATA0_REG, +}; + +static void apply_repeat_encoding(const uint8_t *in_bytes, uint32_t *out_words, size_t in_bytes_len); + // Update Efuse timing configuration static esp_err_t esp_efuse_set_timing(void) { @@ -71,6 +83,31 @@ static esp_err_t esp_efuse_set_timing(void) REG_SET_FIELD(EFUSE_CLK_REG, EFUSE_CLK_SEL1, clk_sel1); return ESP_OK; } + +__attribute__((always_inline)) static inline bool efuse_ll_get_dec_warnings(unsigned block) +{ + if (block == 0 || block > 4) { + return false; + } + uint32_t error_reg = REG_GET_FIELD(EFUSE_DEC_STATUS_REG, EFUSE_DEC_WARNINGS); + if (((error_reg >> (4 * (block - 1))) & 0x0F) != 0) { + return true; + } + return false; +} + +static bool efuse_hal_is_coding_error_in_block(unsigned block) +{ + if (block > 0) { + if (esp_efuse_get_coding_scheme(block) == EFUSE_CODING_SCHEME_3_4) { + if (efuse_ll_get_dec_warnings(block)) { + return true; + } + } + } + return false; +} + #endif // ifndef CONFIG_EFUSE_VIRTUAL // Efuse read operation: copies data from physical efuses to efuse read registers. @@ -84,37 +121,79 @@ void esp_efuse_utility_burn_efuses(void) { #ifdef CONFIG_EFUSE_VIRTUAL ESP_LOGW(TAG, "Virtual efuses enabled: Not really burning eFuses"); - for (int num_block = EFUSE_BLK0; num_block < EFUSE_BLK_MAX; num_block++) { - esp_efuse_coding_scheme_t scheme = esp_efuse_get_coding_scheme(num_block); - if (scheme == EFUSE_CODING_SCHEME_3_4) { - uint8_t buf[COUNT_EFUSE_REG_PER_BLOCK * 4] = { 0 }; - int i = 0; - for (uint32_t addr_wr_block = range_write_addr_blocks[num_block].start; addr_wr_block <= range_write_addr_blocks[num_block].end; addr_wr_block += 4, ++i) { - *((uint32_t*)buf + i) = REG_READ(addr_wr_block); - } - int j = 0; - uint32_t out_buf[COUNT_EFUSE_REG_PER_BLOCK] = { 0 }; - for (int k = 0; k < 4; ++k, ++j) { - memcpy((uint8_t*)out_buf + j * 6, &buf[k * 8], 6); - } - for (int k = 0; k < COUNT_EFUSE_REG_PER_BLOCK; ++k) { - REG_WRITE(range_write_addr_blocks[num_block].start + k * 4, out_buf[k]); - } - } + for (int num_block = EFUSE_BLK_MAX - 1; num_block >= EFUSE_BLK0; num_block--) { int subblock = 0; for (uint32_t addr_wr_block = range_write_addr_blocks[num_block].start; addr_wr_block <= range_write_addr_blocks[num_block].end; addr_wr_block += 4) { virt_blocks[num_block][subblock++] |= REG_READ(addr_wr_block); } } #else - esp_efuse_set_timing(); - // Permanently update values written to the efuse write registers - REG_WRITE(EFUSE_CONF_REG, EFUSE_CONF_WRITE); - REG_WRITE(EFUSE_CMD_REG, EFUSE_CMD_PGM); - while (REG_READ(EFUSE_CMD_REG) != 0) {}; - REG_WRITE(EFUSE_CONF_REG, EFUSE_CONF_READ); - REG_WRITE(EFUSE_CMD_REG, EFUSE_CMD_READ); - while (REG_READ(EFUSE_CMD_REG) != 0) {}; + if (esp_efuse_set_timing() != ESP_OK) { + ESP_LOGE(TAG, "Efuse fields are not burnt"); + } else { + // Permanently update values written to the efuse write registers + // It is necessary to process blocks in the order from MAX-> EFUSE_BLK0, because EFUSE_BLK0 has protection bits for other blocks. + for (int num_block = EFUSE_BLK_MAX - 1; num_block >= EFUSE_BLK0; num_block--) { + esp_efuse_coding_scheme_t scheme = esp_efuse_get_coding_scheme(num_block); + for (uint32_t addr_wr_block = range_write_addr_blocks[num_block].start; addr_wr_block <= range_write_addr_blocks[num_block].end; addr_wr_block += 4) { + if (REG_READ(addr_wr_block) != 0) { + unsigned w_data_len; + unsigned r_data_len; + if (scheme == EFUSE_CODING_SCHEME_3_4) { + esp_efuse_utility_apply_34_encoding((void *)range_write_addr_blocks[num_block].start, (uint32_t *)start_write_addr[num_block], 24); + r_data_len = 24; + w_data_len = 32; + } else if (scheme == EFUSE_CODING_SCHEME_REPEAT) { + apply_repeat_encoding((void *)range_write_addr_blocks[num_block].start, (uint32_t *)start_write_addr[num_block], 16); + r_data_len = 16; + w_data_len = 32; + } else { + r_data_len = (range_read_addr_blocks[num_block].end - range_read_addr_blocks[num_block].start) + sizeof(uint32_t); + w_data_len = (range_write_addr_blocks[num_block].end - range_write_addr_blocks[num_block].start) + sizeof(uint32_t); + memcpy((void *)start_write_addr[num_block], (void *)range_write_addr_blocks[num_block].start, w_data_len); + } + + uint32_t backup_write_data[8]; + memcpy(backup_write_data, (void *)start_write_addr[num_block], w_data_len); + int repeat_burn_op = 1; + bool correct_written_data; + bool coding_error_before = efuse_hal_is_coding_error_in_block(num_block); + bool coding_error_occurred; + + do { + ESP_LOGI(TAG, "BURN BLOCK%d", num_block); + // BURN a block + REG_WRITE(EFUSE_CONF_REG, EFUSE_CONF_WRITE); + REG_WRITE(EFUSE_CMD_REG, EFUSE_CMD_PGM); + while (REG_READ(EFUSE_CMD_REG) != 0) {}; + REG_WRITE(EFUSE_CONF_REG, EFUSE_CONF_READ); + REG_WRITE(EFUSE_CMD_REG, EFUSE_CMD_READ); + while (REG_READ(EFUSE_CMD_REG) != 0) {}; + + bool coding_error_after = efuse_hal_is_coding_error_in_block(num_block); + coding_error_occurred = (coding_error_before != coding_error_after) && coding_error_before == false; + if (coding_error_occurred) { + ESP_LOGE(TAG, "BLOCK%d has an error", num_block); + } + + correct_written_data = esp_efuse_utility_is_correct_written_data(num_block, r_data_len); + if (!correct_written_data || coding_error_occurred) { + ESP_LOGW(TAG, "BLOCK%d: next retry [%d/3]...", num_block, repeat_burn_op); + memcpy((void *)start_write_addr[num_block], (void *)backup_write_data, w_data_len); + } + + } while ((!correct_written_data || coding_error_occurred) && repeat_burn_op++ < 3); + if (!correct_written_data) { + ESP_LOGE(TAG, "Written data are incorrect"); + } + if (coding_error_occurred) { + ESP_LOGE(TAG, "Coding error occurred in block"); + } + break; + } + } + } + } #endif // CONFIG_EFUSE_VIRTUAL esp_efuse_utility_reset(); } @@ -148,44 +227,33 @@ esp_err_t esp_efuse_utility_apply_34_encoding(const uint8_t *in_bytes, uint32_t return ESP_OK; } -static bool read_w_data_and_check_fill(esp_efuse_block_t num_block, uint32_t *buf_w_data) -{ - bool blk_is_filled = false; - int i = 0; - for (uint32_t addr_wr_block = range_write_addr_blocks[num_block].start; addr_wr_block <= range_write_addr_blocks[num_block].end; addr_wr_block += 4, ++i) { - buf_w_data[i] = REG_READ(addr_wr_block); - if (buf_w_data[i] != 0) { - REG_WRITE(addr_wr_block, 0); - blk_is_filled = true; - } - } - return blk_is_filled; -} +#ifndef CONFIG_EFUSE_VIRTUAL -static void read_r_data(esp_efuse_block_t num_block, uint32_t* buf_r_data) +static void apply_repeat_encoding(const uint8_t *in_bytes, uint32_t *out_words, size_t in_bytes_len) { - int i = 0; - for (uint32_t addr_rd_block = range_read_addr_blocks[num_block].start; addr_rd_block <= range_read_addr_blocks[num_block].end; addr_rd_block += 4, ++i) { - buf_r_data[i] = esp_efuse_utility_read_reg(num_block, i); + for (unsigned i = 0; i < 2; i++) { + memcpy(&out_words[i * 4], (uint32_t *)in_bytes, in_bytes_len); } } +#endif // CONFIG_EFUSE_VIRTUAL -// After esp_efuse_write.. functions EFUSE_BLKx_WDATAx_REG were filled is not coded values. -// This function reads EFUSE_BLKx_WDATAx_REG registers, applies coding scheme and writes encoded values back to EFUSE_BLKx_WDATAx_REG. +// This function just checkes that given data for blocks will not rise a coding issue during the burn operation. +// Encoded data will be set during the burn operation. esp_err_t esp_efuse_utility_apply_new_coding_scheme() { - uint8_t buf_w_data[COUNT_EFUSE_REG_PER_BLOCK * 4]; - uint8_t buf_r_data[COUNT_EFUSE_REG_PER_BLOCK * 4]; - uint32_t reg[COUNT_EFUSE_REG_PER_BLOCK]; // start with EFUSE_BLK1. EFUSE_BLK0 - always uses EFUSE_CODING_SCHEME_NONE. for (int num_block = EFUSE_BLK1; num_block < EFUSE_BLK_MAX; num_block++) { esp_efuse_coding_scheme_t scheme = esp_efuse_get_coding_scheme(num_block); - // check and apply a new coding scheme. if (scheme != EFUSE_CODING_SCHEME_NONE) { - memset(buf_w_data, 0, sizeof(buf_w_data)); - memset((uint8_t*)reg, 0, sizeof(reg)); - if (read_w_data_and_check_fill(num_block, (uint32_t*)buf_w_data) == true) { - read_r_data(num_block, (uint32_t*)buf_r_data); + bool is_write_data = false; + for (uint32_t addr_wr_block = range_write_addr_blocks[num_block].start; addr_wr_block <= range_write_addr_blocks[num_block].end; addr_wr_block += 4) { + if (REG_READ(addr_wr_block)) { + is_write_data = true; + } + } + if (is_write_data) { + uint8_t* buf_w_data = (uint8_t*)range_write_addr_blocks[num_block].start; + uint8_t* buf_r_data = (uint8_t*)range_read_addr_blocks[num_block].start; if (scheme == EFUSE_CODING_SCHEME_3_4) { if (*((uint32_t*)buf_w_data + 6) != 0 || *((uint32_t*)buf_w_data + 7) != 0) { return ESP_ERR_CODING; @@ -200,32 +268,14 @@ esp_err_t esp_efuse_utility_apply_new_coding_scheme() return ESP_ERR_CODING; } } - - esp_err_t err = esp_efuse_utility_apply_34_encoding(&buf_w_data[st_offset_buf], reg, 6); - if (err != ESP_OK) { - return err; - } - - int num_reg = (st_offset_buf / 6) * 2; - for (int r = 0; r < 2; r++) { - REG_WRITE(range_write_addr_blocks[num_block].start + (num_reg + r) * 4, reg[r]); - } - i = st_offset_buf + 5; } } } else if (scheme == EFUSE_CODING_SCHEME_REPEAT) { - uint32_t* buf_32 = (uint32_t*)buf_w_data; for (int i = 4; i < 8; ++i) { - if (*(buf_32 + i) != 0) { + if (*((uint32_t*)buf_w_data + i) != 0) { return ESP_ERR_CODING; } } - for (int i = 0; i < 4; ++i) { - if (buf_32[i] != 0) { - REG_WRITE(range_write_addr_blocks[num_block].start + i * 4, buf_32[i]); - REG_WRITE(range_write_addr_blocks[num_block].start + (i + 4) * 4, buf_32[i]); - } - } } } } diff --git a/components/efuse/src/esp32c3/esp_efuse_utility.c b/components/efuse/src/esp32c3/esp_efuse_utility.c index 93cc86e390..9df0471427 100644 --- a/components/efuse/src/esp32c3/esp_efuse_utility.c +++ b/components/efuse/src/esp32c3/esp_efuse_utility.c @@ -83,6 +83,29 @@ static void efuse_program(esp_efuse_block_t block) ets_efuse_clear_program_registers(); efuse_read(); } + +static bool efuse_hal_is_coding_error_in_block(unsigned block) +{ + if (block == 0) { + for (unsigned i = 0; i < 5; i++) { + if (REG_READ(EFUSE_RD_REPEAT_ERR0_REG + i * 4)) { + return true; + } + } + } else if (block <= 10) { + // The order of error in these regs is different only for the C3 chip. + // EFUSE_RD_RS_ERR0_REG: (hi) BLOCK7, BLOCK6, BLOCK5, BLOCK4, BLOCK3, BLOCK2, BLOCK1, ------ (low) + // EFUSE_RD_RS_ERR1_REG: BLOCK9, BLOCK8 + // BLOCK10 is not presented in the error regs. + uint32_t error_reg = REG_READ(EFUSE_RD_RS_ERR0_REG + (block / 8) * 4); + unsigned offset = (block >= 8) ? block - 8 : block; + if (((error_reg >> (4 * offset)) & 0x0F) != 0) { + return true; + } + } + return false; +} + #endif // ifndef CONFIG_EFUSE_VIRTUAL // Efuse read operation: copies data from physical efuses to efuse read registers. @@ -117,9 +140,47 @@ void esp_efuse_utility_burn_efuses(void) ets_efuse_rs_calculate((void *)range_write_addr_blocks[num_block].start, block_rs); memcpy((void *)EFUSE_PGM_CHECK_VALUE0_REG, block_rs, sizeof(block_rs)); } - int data_len = (range_write_addr_blocks[num_block].end - range_write_addr_blocks[num_block].start) + sizeof(uint32_t); + unsigned r_data_len = (range_read_addr_blocks[num_block].end - range_read_addr_blocks[num_block].start) + sizeof(uint32_t); + unsigned data_len = (range_write_addr_blocks[num_block].end - range_write_addr_blocks[num_block].start) + sizeof(uint32_t); memcpy((void *)EFUSE_PGM_DATA0_REG, (void *)range_write_addr_blocks[num_block].start, data_len); - efuse_program(num_block); + + uint32_t backup_write_data[8 + 3]; // 8 words are data and 3 words are RS coding data + memcpy(backup_write_data, (void *)EFUSE_PGM_DATA0_REG, sizeof(backup_write_data)); + int repeat_burn_op = 1; + bool correct_written_data; + bool coding_error_before = efuse_hal_is_coding_error_in_block(num_block); + bool coding_error_occurred; + + do { + ESP_LOGI(TAG, "BURN BLOCK%d", num_block); + efuse_program(num_block); // BURN a block + + bool coding_error_after; + for (unsigned i = 0; i < 5; i++) { + efuse_read(); + coding_error_after = efuse_hal_is_coding_error_in_block(num_block); + if (coding_error_after == true) { + break; + } + } + coding_error_occurred = (coding_error_before != coding_error_after) && coding_error_before == false; + if (coding_error_occurred) { + ESP_LOGE(TAG, "BLOCK%d has an error", num_block); + } + + correct_written_data = esp_efuse_utility_is_correct_written_data(num_block, r_data_len); + if (!correct_written_data || coding_error_occurred) { + ESP_LOGW(TAG, "BLOCK%d: next retry [%d/3]...", num_block, repeat_burn_op); + memcpy((void *)EFUSE_PGM_DATA0_REG, (void *)backup_write_data, sizeof(backup_write_data)); + } + + } while ((!correct_written_data || coding_error_occurred) && repeat_burn_op++ < 3); + if (!correct_written_data) { + ESP_LOGE(TAG, "Written data are incorrect"); + } + if (coding_error_occurred) { + ESP_LOGE(TAG, "Coding error occurred in block"); + } break; } } diff --git a/components/efuse/src/esp32s2/esp_efuse_utility.c b/components/efuse/src/esp32s2/esp_efuse_utility.c index 5dd43b64b1..25557c016b 100644 --- a/components/efuse/src/esp32s2/esp_efuse_utility.c +++ b/components/efuse/src/esp32s2/esp_efuse_utility.c @@ -66,6 +66,27 @@ static esp_err_t esp_efuse_set_timing(void) uint32_t clock_hz = esp_clk_apb_freq(); return ets_efuse_set_timing(clock_hz) ? ESP_FAIL : ESP_OK; } + +static bool efuse_hal_is_coding_error_in_block(unsigned block) +{ + if (block == 0) { + for (unsigned i = 0; i < 5; i++) { + if (REG_READ(EFUSE_RD_REPEAT_ERR0_REG + i * 4)) { + return true; + } + } + } else if (block <= 10) { + // EFUSE_RD_RS_ERR0_REG: (hi) BLOCK8, BLOCK7, BLOCK6, BLOCK5, BLOCK4, BLOCK3, BLOCK2, BLOCK1 (low) + // EFUSE_RD_RS_ERR1_REG: BLOCK10, BLOCK9 + uint32_t error_reg = REG_READ(EFUSE_RD_RS_ERR0_REG + (block / 9) * 4); + unsigned offset = (block >= 9) ? block - 9 : block - 1; + if (((error_reg >> (4 * offset)) & 0x0F) != 0) { + return true; + } + } + return false; +} + #endif // ifndef CONFIG_EFUSE_VIRTUAL // Efuse read operation: copies data from physical efuses to efuse read registers. @@ -100,9 +121,47 @@ void esp_efuse_utility_burn_efuses(void) ets_efuse_rs_calculate((void *)range_write_addr_blocks[num_block].start, block_rs); memcpy((void *)EFUSE_PGM_CHECK_VALUE0_REG, block_rs, sizeof(block_rs)); } - int data_len = (range_write_addr_blocks[num_block].end - range_write_addr_blocks[num_block].start) + sizeof(uint32_t); + unsigned r_data_len = (range_read_addr_blocks[num_block].end - range_read_addr_blocks[num_block].start) + sizeof(uint32_t); + unsigned data_len = (range_write_addr_blocks[num_block].end - range_write_addr_blocks[num_block].start) + sizeof(uint32_t); memcpy((void *)EFUSE_PGM_DATA0_REG, (void *)range_write_addr_blocks[num_block].start, data_len); - ets_efuse_program(num_block); + + uint32_t backup_write_data[8 + 3]; // 8 words are data and 3 words are RS coding data + memcpy(backup_write_data, (void *)EFUSE_PGM_DATA0_REG, sizeof(backup_write_data)); + int repeat_burn_op = 1; + bool correct_written_data; + bool coding_error_before = efuse_hal_is_coding_error_in_block(num_block); + bool coding_error_occurred; + + do { + ESP_LOGI(TAG, "BURN BLOCK%d", num_block); + ets_efuse_program(num_block); // BURN a block + + bool coding_error_after; + for (unsigned i = 0; i < 5; i++) { + ets_efuse_read(); + coding_error_after = efuse_hal_is_coding_error_in_block(num_block); + if (coding_error_after == true) { + break; + } + } + coding_error_occurred = (coding_error_before != coding_error_after) && coding_error_before == false; + if (coding_error_occurred) { + ESP_LOGE(TAG, "BLOCK%d has an error", num_block); + } + + correct_written_data = esp_efuse_utility_is_correct_written_data(num_block, r_data_len); + if (!correct_written_data || coding_error_occurred) { + ESP_LOGW(TAG, "BLOCK%d: next retry [%d/3]...", num_block, repeat_burn_op); + memcpy((void *)EFUSE_PGM_DATA0_REG, (void *)backup_write_data, sizeof(backup_write_data)); + } + + } while ((!correct_written_data || coding_error_occurred) && repeat_burn_op++ < 3); + if (!correct_written_data) { + ESP_LOGE(TAG, "Written data are incorrect"); + } + if (coding_error_occurred) { + ESP_LOGE(TAG, "Coding error occurred in block"); + } break; } } diff --git a/components/efuse/src/esp32s3/esp_efuse_utility.c b/components/efuse/src/esp32s3/esp_efuse_utility.c index 697ad87f30..d6f4c0087e 100644 --- a/components/efuse/src/esp32s3/esp_efuse_utility.c +++ b/components/efuse/src/esp32s3/esp_efuse_utility.c @@ -66,6 +66,27 @@ static esp_err_t esp_efuse_set_timing(void) uint32_t clock_hz = esp_clk_apb_freq(); return ets_efuse_set_timing(clock_hz) ? ESP_FAIL : ESP_OK; } + +static bool efuse_hal_is_coding_error_in_block(unsigned block) +{ + if (block == 0) { + for (unsigned i = 0; i < 5; i++) { + if (REG_READ(EFUSE_RD_REPEAT_ERR0_REG + i * 4)) { + return true; + } + } + } else if (block <= 10) { + // EFUSE_RD_RS_ERR0_REG: (hi) BLOCK8, BLOCK7, BLOCK6, BLOCK5, BLOCK4, BLOCK3, BLOCK2, BLOCK1 (low) + // EFUSE_RD_RS_ERR1_REG: BLOCK10, BLOCK9 + uint32_t error_reg = REG_READ(EFUSE_RD_RS_ERR0_REG + (block / 9) * 4); + unsigned offset = (block >= 9) ? block - 9 : block - 1; + if (((error_reg >> (4 * offset)) & 0x0F) != 0) { + return true; + } + } + return false; +} + #endif // ifndef CONFIG_EFUSE_VIRTUAL // Efuse read operation: copies data from physical efuses to efuse read registers. @@ -100,9 +121,47 @@ void esp_efuse_utility_burn_efuses(void) ets_efuse_rs_calculate((void *)range_write_addr_blocks[num_block].start, block_rs); memcpy((void *)EFUSE_PGM_CHECK_VALUE0_REG, block_rs, sizeof(block_rs)); } - int data_len = (range_write_addr_blocks[num_block].end - range_write_addr_blocks[num_block].start) + sizeof(uint32_t); + unsigned r_data_len = (range_read_addr_blocks[num_block].end - range_read_addr_blocks[num_block].start) + sizeof(uint32_t); + unsigned data_len = (range_write_addr_blocks[num_block].end - range_write_addr_blocks[num_block].start) + sizeof(uint32_t); memcpy((void *)EFUSE_PGM_DATA0_REG, (void *)range_write_addr_blocks[num_block].start, data_len); - ets_efuse_program(num_block); + + uint32_t backup_write_data[8 + 3]; // 8 words are data and 3 words are RS coding data + memcpy(backup_write_data, (void *)EFUSE_PGM_DATA0_REG, sizeof(backup_write_data)); + int repeat_burn_op = 1; + bool correct_written_data; + bool coding_error_before = efuse_hal_is_coding_error_in_block(num_block); + bool coding_error_occurred; + + do { + ESP_LOGI(TAG, "BURN BLOCK%d", num_block); + ets_efuse_program(num_block); // BURN a block + + bool coding_error_after; + for (unsigned i = 0; i < 5; i++) { + ets_efuse_read(); + coding_error_after = efuse_hal_is_coding_error_in_block(num_block); + if (coding_error_after == true) { + break; + } + } + coding_error_occurred = (coding_error_before != coding_error_after) && coding_error_before == false; + if (coding_error_occurred) { + ESP_LOGE(TAG, "BLOCK%d has an error", num_block); + } + + correct_written_data = esp_efuse_utility_is_correct_written_data(num_block, r_data_len); + if (!correct_written_data || coding_error_occurred) { + ESP_LOGW(TAG, "BLOCK%d: next retry [%d/3]...", num_block, repeat_burn_op); + memcpy((void *)EFUSE_PGM_DATA0_REG, (void *)backup_write_data, sizeof(backup_write_data)); + } + + } while ((!correct_written_data || coding_error_occurred) && repeat_burn_op++ < 3); + if (!correct_written_data) { + ESP_LOGE(TAG, "Written data are incorrect"); + } + if (coding_error_occurred) { + ESP_LOGE(TAG, "Coding error occurred in block"); + } break; } } diff --git a/components/efuse/src/esp_efuse_utility.c b/components/efuse/src/esp_efuse_utility.c index be590a61f1..0eeebef960 100644 --- a/components/efuse/src/esp_efuse_utility.c +++ b/components/efuse/src/esp_efuse_utility.c @@ -354,3 +354,29 @@ static bool check_range_of_bits(esp_efuse_block_t blk, int offset_in_bits, int s } return true; } + +bool esp_efuse_utility_is_correct_written_data(esp_efuse_block_t block, unsigned r_data_len) +{ + uint32_t* w_data = (uint32_t*)range_write_addr_blocks[block].start; + uint32_t* r_data = (uint32_t*)range_read_addr_blocks[block].start; + + bool correct_written_data = memcmp(w_data, r_data, r_data_len) == 0; + if (correct_written_data) { + ESP_LOGI(TAG, "BURN BLOCK%d - OK (write block == read block)", block); + } else { + correct_written_data = true; + for (unsigned i = 0; i < r_data_len / 4; i++) { + if ((*(r_data + i) & *(w_data + i)) != *(w_data + i)) { + correct_written_data = false; + break; + } + } + if (correct_written_data) { + ESP_LOGI(TAG, "BURN BLOCK%d - OK (all write block bits are set)", block); + } + } + if (!correct_written_data) { + ESP_LOGE(TAG, "BURN BLOCK%d - was not successful", block); + } + return correct_written_data; +}