Протокол загрузчика ESP32 |
![]() |
Добавил(а) microsin | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
В этой статье приведен перевод документации [1], описывающей протокол UART-загрузчика микроконтроллеров ROM Loader (ESP32 UART bootloader ROM), заглушка загрузчика stub loader, работающая в ОЗУ, и утилита для работы с загрузчиком esptool. UART bootloader запускается сразу после деактивации сигнала сброса, если удерживаются соответствующем логическом уровне так называемые выводы управления загрузкой (strapping pins [4]). Протокол ESP32 ROM-загрузчика подобен протоколу ESP8266, хотя у ESP32 добавлены некоторые дополнительные команды, и в нем несколько отличается поведение. По умолчанию утилита esptool выгрузит программный загрузчик (stub loader) в IRAM чипа. После этого stub loader заменит ROM-загрузчик для всех последующих операций взаимодействия. Это во многом стандартизирует поведение процесса перепрошивки. Можно передать опцию --no-stub в командную строку esptool, чтобы запретить использование stub loader (подробнее см. документацию [3]). Замечание: существуют различия последовательного протокола между чипами ESP! Чтобы переключиться на оригинал документации по описанию протокола, на страничке [1] в левом верхнем углу из выпадающего списка выберите нужную модель чипа. Примечание: исходный код stub loader можно найти в папке components/esptool_py/esptool/flasher_stub/ среды программирования ESP-IDF версии 4.4.1, либо на Github в репозитории [8]. [Описание пакета] Компьютер хоста посылает чипу ESP пакет команды запроса (Command Packet). Чип ESP отвечает на эти команды пакетом (Response Packet), в котором включена информация статуса и любые данные в качестве полезной нагрузки. Протокол низкого уровня. Протокол загрузчика использует пакет кадра SLIP для передачи данных в обоих направлениях. Каждый пакет SLIP начинается и заканчивается байтом 0xC0. Внутри пакета все появляющиес байты 0xC0 и 0xDB заменяются соответственно на последовательности байт 0xDB 0xDC и 0xDB 0xDD. Эта замена происходит после вычисления контрольной суммы и длины пакета, поэтому длина пакета может быть больше, чем значение поля Size, описанного далее (см. таблицу 1). Command Packet. Каждая команда протоколе SLIP загрузчика инициируется хостом с помощью пакета команды (Command Packet), и в ответ на него приходит пакет ответа (Response Packet). Внутри пакет состоит из заголовка и тела данных переменной длины. Все многобайтные поля представлены в последовательности старшинства байт little-endian [7]. Таблица 1. Формат пакета команды (запрос).
Response Packet. Каждая принятая загрузчиком команда приводит к передаче ответа в виде SLIP-пакета от чипа ESP к хосту. Содержимое пакета ответа следующее: Таблица 2. Формат пакета ответа.
Байты состояния (Status Bytes). Последние байты полезной нагрузки Data показывают статус команды. Для stub loader последние 2 байта статуса следующие (большинство команд возвратят как минимум 2 байта полезной нагрузки Data): Таблица 3. Байты состояния команды для stub loader.
Для загрузчика ESP32 ROM (только для него, это не относится к stub loader) используются последние 4 байта, но только первые два из них содержат информацию статуса:
ROM Loader Errors. ROM-загрузчик посылает следующие коды ошибок. Таблица 4. Коды ошибок ROM Loader.
Stub Loader Status & Error. Если используется stub loader: • В статусе ответа всегда используются 2 байта, независимо от типа чипа. /* Коды ошибки Stub Loader (см. [8]) */
typedef enum { ESP_OK = 0, ESP_BAD_DATA_LEN = 0xC0, ESP_BAD_DATA_CHECKSUM = 0xC1, ESP_BAD_BLOCKSIZE = 0xC2, ESP_INVALID_COMMAND = 0xC3, ESP_FAILED_SPI_OP = 0xC4, ESP_FAILED_SPI_UNLOCK = 0xC5, ESP_NOT_IN_FLASH_MODE = 0xC6, ESP_INFLATE_ERROR = 0xC7, ESP_NOT_ENOUGH_DATA = 0xC8, ESP_TOO_MUCH_DATA = 0xC9, ESP_CMD_NOT_IMPLEMENTED = 0xFF, } esp_command_error; После отправки команды хост должен продолжать чтение пакетов ответа, пока не будет получен пакет ответа, где в поле Command окажется то же самое значение, что и в поле Command запроса, либо пока не истечет таймаут приема пакетов ответа. [Команды загрузчика] Таблица 5. Команды, поддерживаемые stub loader и ROM loader.
Примечание: память SPI flash это необязательно внешняя микросхема, это может быть память, размещенная в корпусе микроконтроллера, но она все равно подключается через SPI. Таблица 6. Команды, которые поддерживает только stub loader.
Контрольная сумма. Поле контрольной суммы игнорируется (может быть нулем) для всех команд, кроме MEM_DATA, FLASH_DATA и FLASH_DEFL_DATA. У каждого из пакетов команды _DATA (наподобие FLASH_DEFL_DATA, MEM_DATA) используется одинаковый формат данных полезной нагрузки ("data payload"):
Контрольная сумма вычисляется только от последних данных для записи, но не от первых 16 байт полезной нагрузки. Чтобы вычислить контрольную сумму, начните с seed-значения 0xEF, и выполняйте на нем операцию XOR (исключающее ИЛИ) для каждого отдельного байта поля "данных для записи". 8-разрядный результат сохраняется в поле контрольной суммы заголовка пакета (в виде 32-битного значения little endian). Замечание: из-за того, что такое вычисление контрольной суммы не является адекватной гарантией целостности данных, была добавлена команда SPI_FLASH_MD5 для проверки содержимого flash после прошивки. Всегда рекомендуется использовать эту команду, см. далее "Проверка выгруженных данных". [Функциональное описание] Начальная синхронизация • Чип ESP после сброса переходит в режим последовательной загрузки (UART bootloader mode). Хост начинает передавать команды SYNC. В этих командах находится поле достаточной длины, чтобы которое чип ESP использует для автодетекта сконфигурированной скорости. ESP32 всегда инициализируется с начальной скоростью 115200 бит/сек (bps). Однако пакеты синхронизации могут быть отправлены на любой скорости, и по ним автодетект скорости настроит периферийное устройство UART. Запись данных (включает режимы RAM Download, Flash Download, Compressed Flash Download). • RAM Download (MEM_BEGIN, MEM_DATA, MEM_END) загружает данные в оперативную память чипа ESP и (опционально) запускает их. Все три перечисленные выше режима следуют одному и тому же шаблону действий: • Посылается команда _BEGIN (FLASH_BEGIN, и т. п.), которая содержит базовые параметры для размера стираемой области flash, начального адреса для записи и т. д. Коду выгрузки также нужно указать, сколько "блоков" данных (т. е. отдельных пакетов данных) будет отправлено, и какой размер у каждого такого пакета. Нет необходимости перед командой записи flash посылать команды стирания. ROM-загрузчики стирают записываемый регион в ответ на команду FLASH_BEGIN. Код stub loader делает стирание flash по мере записи данных, чтобы максимизировать общую эффективность прошивки (каждый блок данных считывается в RAM, в то время как предыдущий блок одновременно записывается во flash, и операции стирания 4KB и 64KB осуществляются перед записью во flash). Размер блока должен быть выбран достаточно малым, чтобы поместиться в RAM чипа. Утилита esptool использует 16KB, что дает хорошую производительность при использовании stub loader. Проверка выгруженных данных. В протоколе выгрузке используется 8-разрядная контрольная сумма, которая недостаточна для гарантии получения корректного содержимого памяти flash после выгрузки. Посылающие данные код должен использовать команду SPI_FLASH_MD5 или другой надежный метод верификации содержимого flash. Команда SPI_FLASH_MD5 передает начальный адрес в flash и размер данных для вычисления хэша MD5. В полезной нагрузке ответа передается значение MD5 перед байтами статуса. Обратите внимание, что ESP32 ROM Loader возвращает md5sum как 32 байта ASCII, кодирующие HEX-значение MD5, в то время как stub loader возвратит md5sum как 16 сырых байт данных MD5, за которыми идут 2 байта статуса. [Команды конфигурации SPI] Команда SPI Attach. Команда SPI_ATTACH разрешает интерфейс SPI flash. Она принимает 32-битную полезную нагрузку данных, определяющую, какое периферийное устройство и какие выводы должны использоваться для подключения к SPI flash. Для ESP32 stub loader требуется послать эту команду перед началом взаимодействия с памятью SPI flash.
Вариант "Интерфейс SPI flash по умолчанию" использует выводы, сконфигурированные через efuse-биты SPI_PAD_CONFIG_xxx (если не установлены, то все они соответствуют нулям, и тогда используются выводы по умолчанию, определенные в даташите). Когда записываются 6-битные значения для каждой выбранной ножки, то это значение кодируется следующим образом: • Номера выводов 0 .. 30 кодируются их номерами. Только для ESP32 ROM Loader у этой команды существуют дополнительные 4 байта в полезной нагрузке. Все эти байты должны быть установлены в 0. Установка параметров SPI. Команда SPI_SET_PARAMS устанавливает некоторые параметры подключенного чипа SPI flash (его размер, и т. д.). Все значения, которые передаются, за исключением общего размера, жестко вшиты в код (hardcoded), и большинство не используется при записи во flash. См. функцию flash_set_parameters в утилите esptool для значений, которые она посылает. 32-bit Read/Write. Команды 32-битных чтения и записи (READ_REG, WRITE_REG) позволяют выполнять выровненные на 32-разрядное слово операции обращения к данным памяти и регистров микроконтроллера. Эти команды могут использоваться для произвольных манипуляций периферийными устройствами микроконтроллера. Например, реализована функциональность "flash id" утилиты esptool путем манипуляции регистрами периферийного устройства SPI, чтобы отправить команду JEDEC flash ID в кристалл памяти flash, и прочитать ответ от неё. Чтение flash. В коде stub loader реализована команда READ_FLASH. Эта команда ведет себя по-другому в сравнении с другими командами, включая READ_FLASH команду ROM Loader-а: • Хост посылает команду READ_FLASH, и полезная нагрузка данных содержит смещение, размер чтения, размер каждого отдельного пакета данных, и максимальное количество "не подтвержденных" пакетов данных, которые могут быть отправлены одновременно. После завершения процесса чтения flash код stub loader вернется к нормальному функционированию по типу команда/ответ. Команда чтения у ROM Loader более "нормальная", то работает намного медленнее. [Последовательный обмен esptool для трассировки] Утилита esptool поддерживает опцию --trace, которая может быть предоставлена в первой группе аргументов (перед командой). Эта опция приведет к выводу дампа всего передаваемого и принимаемого трафика, который проходит в консоль через последовательный порт. Вот кусок трассировки, показывающий команду READ_REG и ответ на неё: TRACE +0.000 command op=0x0a data len=4 wait_response=1 timeout=3.000 data=1400f43f TRACE +0.000 Write 14 bytes: c0000a0400000000001400f43fc0 TRACE +0.005 Read 1 bytes: c0 TRACE +0.000 Read 11 bytes: 010a0200620100000000c0 TRACE +0.000 Received full packet: 010a0200620100000000 The +X.XXX value is the time delta (in seconds) since the last trace line. Печатаемые значения выводятся в HEX-формате. Если больше 16 байт выводится одновременно, то производится разделение отображения на 2 части, где байты в HEX-формате показаны слева, а соответствующие им символы ASCII справа. "Не печатаемые" символы при этом отображаются ASCII-символом точки '.'. Обратите внимание, что в логах присутствуют несколько слоев протокола. Строки "Write X bytes" показывают, какое точное количество байт отправлено "по проводу", включая оформление кадров SLIP. Подобным образом строки "Read X bytes" показывают, сколько байт было прочитано по последовательному каналу, включая оформление кадров SLIP. Как только прочитан полный пакет SLIP, те же самые байты - что и полезная нагрузка SLIP с любыми удаленными дополнительными кодами - появятся в строках лога "Received full packet". Вот второй пример, показывающий начальную последовательность синхронизации (порция байт 0x55, которые соответствуют ASCII-коду буквы 'U'): TRACE +0.000 Write 46 bytes: c000082400000000 0007071220555555 | ...$........ UUU 5555555555555555 5555555555555555 | UUUUUUUUUUUUUUUU 5555555555555555 5555555555c0 | UUUUUUUUUUUUU. TRACE +0.011 Read 1 bytes: c0 TRACE +0.000 Read 63 bytes: 0108040007122055 00000000c0c00108 | ...... U........ 0400071220550000 0000c0c001080400 | .... U.......... 0712205500000000 c0c0010804000712 | .. U............ 205500000000c0c0 01080400071220 | U............ TRACE +0.000 Received full packet: 010804000712205500000000 TRACE +0.000 Received full packet: 010804000712205500000000 Важное замечание: если вы не планируете использовать stub loader с утилитой esptool, то передайте --no-stub --trace, чтобы увидеть взаимодействие только с внутренним ПЗУ-загрузчиком чипа (built-in ROM loader). Иначе трассировка покажет полный бинарный поток загрузчика. В дополнение к этой функции трассировки у большинства операционных систем есть фича "system call trace" или "port trace", которую можно использовать для получения дампа обмена данными через последовательный интерфейс. [Ссылки] 1. ESP32 bootloader ROM Serial Protocol site:espressif.com. |