Библиотека eFuse Manager была разработана для структурирования доступа к битам eFuse [2], и для упрощения доступа к этим битам. Библиотека работает с битами eFuse по структурному имени, назначенном в таблице eFuse. В этой статье представлены некоторые концепции, используемые в eFuse Manager (перевод документации [1]).
На кристалле ESP32 имеется определенное количество так называемых бит eFuse, которые могут хранить в себе системные и пользовательские параметры. Каждый eFuse это однобитное поле, которое можно запрограммировать в 1, после чего его состояние обратно вернуть в 0 будет невозможно. Некоторые системные параметры используют эти биты eFuse напрямую в модулях аппаратуры, и находятся в специальных местах (например EFUSE_BLK0). Более подробно см. техническое руководство ESP32 Technical Reference Manual -> eFuse Controller (файл esp32_technical_reference_manual_en.pdf [5]). Также см. перевод описания контроллера eFuse из этого руководства [2].
Всего у ESP32 есть 4 блока eFuse, каждый размером 256 бит (но не все эти биты доступны):
EFUSE_BLK0: полностью используются в системных целях. EFUSE_BLK1: используется для ключа шифрования flash. Если функция шифрования (Flash Encryption) не используется, то этот блок бит может использоваться для другой цели. EFUSE_BLK2: используется для ключа технологии безопасной загрузки (security boot [3]). Если функция Secure Boot не используется, то этот блок может использоваться для другой цели. EFUSE_BLK3: может частично использоваться для пользовательского MAC-адреса, или использоваться полностью для приложения пользователя. Обратите внимание, что некоторые биты уже используются в ESP IDF [4].
Каждый блок поделен на 8 регистров, каждый из 32 бит.
[Компонент eFuse Manager]
У компонента есть API-функции для чтения и записи полей бит. Доступ к этим полям осуществляется через структуры, которые описывают места размещения бит eFuse в блоках. Компонент предоставляет возможность формирования полей любой длины и любого количества отдельных бит. Описание полей сделано в файле CSV, в виде таблицы. Для генерации табличной формы (файл CSV) в исходном коде C используется скрипт efuse_table_gen.py. Этот скрипт проверяет файл CSV на уникальность имен полей и пересечение бит, в случае использования пользовательского файла из директории проекта пользователя, утилита проверит общий CSV-файл.
Файлы CSV:
common (общий файл esp_efuse_table.csv) - содержит поля eFuse, используемые средой разработки ESP IDF. Генерация исходного кода C должна быть сделана вручную, когда содержимое этого файла было изменено (путем запуска команды idf.py efuse-common-table). Обратите внимание, что изменения в этом файле могут привести к некорректному функционированию.
custom (использование этого файла необязательно, это можно разрешить опцией CONFIG_EFUSE_CUSTOM_TABLE) - содержит поля eFuse, которые используются пользователем в своем приложении. Генерация исходного кода C должна быть выполнена вручную, когда этот файл изменяется, путем запуска команды idf.py efuse-custom-table.
[Описание CSV-файла]
CSV-файл содержит описание полей eFuse. В простом случае одно поле имеет одну строку описания. Заголовок таблицы:
Отдельные параметры в CSV-файле означают следующее.
field_name: поле имени. К имени будет добавлен префикс ESP_EFUSE_, и это имя будет доступно в коде программы, его можно использовать для доступа к полям eFuse. Имена всех полей должны быть уникальными. Если строка содержит пустое имя, то эта строка будет комбинироваться с предыдущим полем. Это позволит Вам установить произвольный порядок бит в поле, а также расширить поле (см. поле MAC_FACTORY в таблице common). Поле field_name поддерживает структурный формат с использованием точки '.', чтобы показать, что поле принадлежит другому полю (см. WR_DIS и RD_DIS в таблице common).
efuse_block: номер блока. Определяет, где должны быть размещены биты для этого поля. Доступны номера блоков EFUSE_BLK0 .. EFUSE_BLK3.
bit_start: номер начального бита (0..255). Поле bit_start может быть опущено, тогда оно будет установлено на основании предыдущей записи, т. е. её bit_start + bit_count, если эта запись принадлежит тому же самому блоку efuse_block. Иначе, если efuse_block отличается, или это первая запись, будет сгенерирована ошибка.
bit_count: количество бит для использования в этом поле (1 .. n). Этот параметр обязателен, и не может быть опущен. Это поле также может быть значением MAX_BLK_LEN, тогда его длина будет равна максимальной длине блока, с учетом схемы кодирования (применимо для полей ключей шифрования ESP_EFUSE_SECURE_BOOT_KEY и ESP_EFUSE_ENCRYPT_FLASH_KEY). Значение MAX_BLK_LEN зависит от CONFIG_EFUSE_CODE_SCHEME_SELECTOR (опция выбора схемы кодирования): для None значение 256, для 3/4 значение 192, для REPEAT значение 128.
comment: комментарий, который также будет помещен в заголовочный файл C. Комментарий может быть опущен.
Если необходим не последовательный порядок следования бит для описания поля, то описание поля в следующих строка должно быть продолжено без указания имени field_name. Это покажет, что следующие записи будут принадлежать одному полю. Ниже показан пример двух полей MAC_FACTORY и MAC_FACTORY_CRC:
# Factory MAC address #
#######################
MAC_FACTORY, EFUSE_BLK0, 72, 8, Factory MAC addr [0]
, EFUSE_BLK0, 64, 8, Factory MAC addr [1]
, EFUSE_BLK0, 56, 8, Factory MAC addr [2]
, EFUSE_BLK0, 48, 8, Factory MAC addr [3]
, EFUSE_BLK0, 40, 8, Factory MAC addr [4]
, EFUSE_BLK0, 32, 8, Factory MAC addr [5]
MAC_FACTORY_CRC, EFUSE_BLK0, 80, 8, CRC8 for factory MAC address
Это поле будет доступно в коде как ESP_EFUSE_MAC_FACTORY и ESP_EFUSE_MAC_FACTORY_CRC.
[Структурированные поля eFuse]
Структурированные поля выглядят наподобие WR_DIS.RD_DIS, где точка говорит от том, что поле принадлежит родительскому полю WR_DIS, и не может выйти за диапазон родительского поля.
WR_DIS, EFUSE_BLK0, 0, 32, защита от записи ..
WR_DIS.RD_DIS, EFUSE_BLK0, 0, 1, .. для RD_DIS
WR_DIS.FIELD_1, EFUSE_BLK0, 1, 1, .. для FIELD_1
WR_DIS.FIELD_2, EFUSE_BLK0, 2, 4, .. для FIELD_2 (включает B1 и B2)
WR_DIS.FIELD_2.B1, EFUSE_BLK0, 2, 2, .. для FIELD_2.B1
WR_DIS.FIELD_2.B2, EFUSE_BLK0, 4, 2, .. для FIELD_2.B2
WR_DIS.FIELD_3, EFUSE_BLK0, 5, 1, .. для FIELD_3
WR_DIS.FIELD_3.ALIAS, EFUSE_BLK0, 5, 1, .. для FIELD_3 (просто псевдоним WR_DIS.FIELD_3)
WR_DIS.FIELD_4, EFUSE_BLK0, 7, 1, .. для FIELD_4
Есть возможность использовать несколько уровней структурированных полей, как WR_DIS.FIELD_2.B1 и B2. Эти поля не должны пересекать друг друга, и их биты должны быть в пределах диапазонов двух полей WR_DIS и WR_DIS.FIELD_2.
Можно создавать псевдонимы для полей, находящихся в одном и том же диапазоне бит, см. WR_DIS.FIELD_3 и WR_DIS.FIELD_3.ALIAS.
Имена IDF для структурированных полей efuse должны быть уникальными. Утилита efuse_table_gen будет генерировать конечные имена, где точка будет заменена на символ подчеркивания '_'. Имена для использования в IDF: ESP_EFUSE_WR_DIS, ESP_EFUSE_WR_DIS_RD_DIS, ESP_EFUSE_WR_DIS_FIELD_2_B1, и т. д.
[Утилита efuse_table_gen.py]
Это скрипт на языке Python, который может генерировать исходный код на языке C из файла CSV, при этом будет сделана проверка его полей. Прежде всего производится проверка уникальности имен и наложение друг на друга полей бит. Если используется дополнительный пользовательский файл, то он будет проверен вместе с существующим общим файлом common (esp_efuse_table.csv). В случае ошибок отобразится сообщение и строка, которая привела к ошибке. Генерируемые файлы языка C будут содержать структуры типа esp_efuse_desc_t.
Для генерации файлов common используйте команду idf.py efuse-common-table, или:
cd $IDF_PATH/components/efuse/
./efuse_table_gen.py esp32/esp_efuse_table.csv
После завершения генерации в папке $IDF_PATH/components/efuse/esp32 будут созданы (или обновлены) файлы esp_efuse_table.c и include\esp_efuse_table.h.
Для генерации файлов пользователя custom используйте команду idf.py efuse-custom-table, или:
cd $IDF_PATH/components/efuse/
./efuse_table_gen.py esp32/esp_efuse_table.csv PROJECT_PATH/main/esp_efuse_custom_table.csv
После генерации в папке проекта PROJECT_PATH/main будет создан файл esp_efuse_custom_table.c, и в подкаталоге include будет создан заголовочный файл esp_efuse_custom_table.h.
Для использования сгенерированных полей в коде нужно будет подключить два файла:
#include "esp_efuse.h"
// В случае использования полей custom надо будет здесь подключить
// файл esp_efuse_custom_table.h вместо esp_efuse_table.h:
#include "esp_efuse_table.h"
[Поддерживаемые схемы кодирования]
eFuse использует 3 варианта схем кодирования, выбор схемы определяется значением опции CONFIG_EFUSE_CODE_SCHEME_SELECTOR:
Схема кодирования влияет только на блоки EFUSE_BLK1, EFUSE_BLK2 и EFUSE_BLK3. Блок EUSE_BLK0 всегда использует схему None. Схема кодирования меняет количество бит, которое можно записать в блок, длина блока постоянна и равна 256, некоторые их этих бит используются для кодирования, и они недоступны для пользователя.
Когда используется схема кодирования, длина полезной нагрузки ограничивается (подробнее см. раздел 20.3.1.3 System Parameter coding_scheme документа esp32_technical_reference_manual_en.pdf [5], или перевод [2]):
Используемую схему кодирования чипа можно определить следующими способами:
• Запуском команды espefuse.py -p PORT summary. • Из логов утилиты esptool (после прошивки). • Вызовом в коде функции esp_efuse_get_coding_scheme() для блока EFUSE_BLK3.
Используемую схему кодирования для библиотеки eFuse Manager в проекте можно поменять/проверить запуском команды idf.py menuconfig, после чего надо зайти в раздел настроек Component config -> eFuse Bit Manager -> Coding Scheme Compability:
Flash voltage (VDD_SDIO) determined by GPIO12 on reset (High for 1.8V, Low/NC for 3.3V).
Таблицы eFuse всегда должны соответствовать схеме кодирования в чипе. Существует опция для выбора кодирования CONFIG_EFUSE_CODE_SCHEME_SELECTOR в Kconfig. Когда генерируются файлы исходного кода, если Ваши таблицы не следуют схеме кодирования, то будет показано сообщение об ошибке. Настройте длину или смещение полей. Если Ваша программа была скомпилирована с кодированием None, и в чипе используется кодирование 3/4, то может произойти ошибка ESP_ERR_CODING, когда вызывается API (поле находится вне границ блока). Если поле соответствует новым границам блока, то API будет работать без ошибок.
Также схема кодирования 3/4 накладывает ограничения на запись бит, принадлежащих одному элементу кодирования. Весь блок длиной 256 делится на 4 элемента кодирования, и каждый элемент кодирования состоит из 6 байт с полезными данными и 2 сервисных байт. Эти 2 сервисных байта содержат контрольную сумму от предыдущих 6 байт данных. Получается, что в один элемент кодирования можно записать только одно поле. Повторяющиеся перезаписи одного и того же элемента кодирования запрещено. Однако если была сделана предварительная запись, или через функцию esp_efuse_write_block(), то возможно чтение полей, принадлежащих одному элементу кодирования.
В случае схемы кодирования 3/4 процесс записи делится на элементы кодирования, и мы не можем использовать обычный режим записи для некоторых полей. Мы можем подготовить все данные для записи, и прошить их за одну операцию. Вы также можете использовать этот режим и для схемы кодирования None, но для этого режима такой метод записи не является обязательным. Он важен для схемы 3/4. Пакетный режим записи блокирует операции esp_efuse_read_... (чтение eFuse).
После изменения схемы кодирования запустите команды efuse_common_table и efuse_custom_table, чтобы проверить таблицы новой схемы кодирования.
Чтобы записать некоторые поля в один блок, или разные блоки за одну операцию, необходимо использовать пакетный режим записи (batch writing mode). Сначала установите этот режим вызовом функции esp_efuse_batch_write_begin(), затем запишите некоторые поля как обычно, используя функции esp_efuse_write_... И в завершение прошейте их, для чего вызовите функцию esp_efuse_batch_write_commit(). Этот вызов прошьет подготовленные данные в блоки eFuse, и запретит batch recording mode.
Замечание: если в блоке eFuse уже есть записанные данные в схеме кодирования 3/4 или Repeat, то невозможно записать что-либо дополнительно (даже если требуемые биты пустые), без повреждения предыдущих закодированных данных. Эти данные кодирования будут полностью перезаписаны новыми данными кодирования, и полностью уничтожены (полезная нагрузка eFuse останется неповрежденной). Это может относиться к параметрам CUSTOM_MAC, SPI_PAD_CONFIG_HD, SPI_PAD_CONFIG_CS, и т. д. Пожалуйста, обратитесь к Espressif, чтобы закупить чипы/модули с требуемыми, предварительно записанными eFuse.
ТОЛЬКО ДЛЯ ТЕСТИРОВАНИЯ (НЕ РЕКОМЕНДУЕТСЯ): Вы можете игнорировать или подавить ошибки, которые нарушают схему кодирования данных, чтобы прошить необходимые биты в блок eFuse.
[eFuse API]
Заголовочный файл:
components/efuse/esp32/include/esp_efuse.h
Доступ к полям осуществляется через указатель на структуру описания. API-функции для выполнения базовых операций:
Функция
Описание
esp_efuse_read_field_blob()
Возвратит массив чтения бит eFuse.
esp_efuse_read_field_cnt()
Возвратит количество бит, запрограммированных в 1.
esp_efuse_write_field_blob()
Записывает массив.
esp_efuse_write_field_cnt()
Записывает требуемое количество бит в 1.
esp_efuse_get_field_size()
Возвратит количество бит по имени поля.
esp_efuse_read_reg()
Возвратит значения регистра eFuse.
esp_efuse_write_reg()
Записывает значение в регистр eFuse.
esp_efuse_get_coding_scheme()
Возвратит схему кодирования для блоков eFuse.
esp_efuse_read_block()
Возвратит ключ для блока eFuse, начиная с указанного смещения и требуемого размера.
esp_efuse_write_block()
Запишет ключ для блока eFuse, начиная с указанного смещения и требуемого размера.
esp_efuse_batch_write_begin()
Установит пакетный режим для записи полей.
esp_efuse_batch_write_commit()
Запишет все подготовленные поля данных в пакетном режиме, и выполнит выход из пакетного режима записи.
esp_efuse_batch_write_cancel()
Сбрасывает пакетный режим записи и подготовленные для этого данные.
esp_efuse_get_key_dis_read()
Возвращает защиту от чтения для ключа блока.
esp_efuse_set_key_dis_read()
Устанавливает защиту от чтения для ключа блока.
esp_efuse_get_key_dis_write()
Возвратит защиту от записи для ключа блока.
esp_efuse_set_key_dis_write()
Установит защиту от записи для ключа блока.
esp_efuse_get_key_purpose()
Возвратит текущий набор предназначения для ключа блока.
esp_efuse_write_key()
Программирует данные ключа в блок eFuse.
esp_efuse_write_keys()
Программирует ключи для не используемых блоков eFuse.
esp_efuse_find_purpose()
Найдет ключ блока с определенным набором предназначения.
esp_efuse_get_keypurpose_dis_write()
Возвратит защиту от чтения поля предназначения ключа для ключа блока eFuse (для ESP32 всегда true).
esp_efuse_key_block_unused()
Возвратит true, если ключ блока не используется, иначе false.
Для часто используемых полей сделаны специальные функции, наподобие esp_efuse_get_chip_ver(), esp_efuse_get_pkg_ver().
Подробное описание функций см. в документации [1].
[Как добавить новое поле]
1. Найдите свободные биты для поля. Для этого просмотрите файл esp_efuse_table.csv, или запустите команду idf.py show-efuse-table. Также можно запустить следующую команду:
Свободны биты, которые не попали в квадратные скобки (биты в блоке EFUSE_BLK0 зарезервированы для Espressif). Все поля проверяются на перекрытие друг на друга.
2. Заполните строку для нового поля: field_name, efuse_block, bit_start, bit_count, comment.
3. Запустите команду show_efuse_table, чтобы проверить таблицу eFuse. Чтобы сгенерировать файлы исходного кода запустите команду efuse_common_table или efuse_custom_table.
[Отладка eFuse и Unit-тесты]
Виртуальные eFuse. Опция CONFIG_EFUSE_VIRTUAL для Kconfig виртуализирует значения eFuse в среде eFuse Manager, так что записи эмулируются, и реальные значения eFuse не меняются. Это может быть полезно для отладки приложений и unit-тестов. Во время запуска приложения (startup), значения eFuse копируются в RAM. Все операции с eFuse (чтение и запись) выполняются с RAM вместо реальных регистров eFuse.
В дополнение к опции CONFIG_EFUSE_VIRTUAL option есть еще опция CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH, которая добавляет функцию сохранения значений eFuse в памяти flash. Для использования этого режима таблица разделов partition_table должна содержать раздел efuse. Файл partition.csv: "efuse_em, data, efuse, , 0x2000,". Во время startup, значения eFuse копируются из flash или, в случае когда flash пустая, из реальных eFuse в RAM, и затем обновляется flash. Эта опция позволяет сохранять значения eFuse между перезагрузками (с этой опцией можно проверить функции secure_boot и flash_encryption).
espefuse.py. Инструментарий esptool включает полезную утилиту для чтения/записи бит ESP32 eFuse - espefuse.py.