Программирование ARM BL602: протокол прошивки Thu, November 21 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


BL602: протокол прошивки Печать
Добавил(а) microsin   

[Формат загружаемого файла UART/SDIO (boot image format)]

Микроконтроллеры Bouffalo серии BL602 поддерживают перепрошивку через UART/SDIO. Секция исполняемой программы через интерфейс UART/SDIO может быть загружена в RAM для запуска. Структура загружаемой программы должна удовлетворять формату загрузчика (BL602 Bootrom). Для приложений, которые не активируют настройки безопасности (т. е. приложения, где не разрешены encryption и signatures), загружаемый образ показан на следующем рисунке.

BL602 boot image no encryption no signature fig01

Рис. 1. UART/SDIO boot image, без шифрования и цифровой подписи (no encryption, no signature).

Загружаемый образ состоит из 3 частей:

BootInfo. Здесь в основном находится Magic Code информации загрузки (BootInfo), информация конфигурации flash (загрузка через UART не требует информации flash, это только совместимо с образами Flash boot), информация конфигурации PLL, информация параметров boot, и информация конфигурации mirror.

SegmentHeader. Информация заголовка секции загруженной программы или секции данных, используется в основном для указания секции данных, которая должна быть передана следующей, по какому адресу в памяти она должна быть размещена, длина данных и другая информация.

SegmentData. Основное тело блока данных сегмента загружаемой программы или сегмента данных.

Здесь может быть несколько пар SegmentHeader и SegmentData, и информация конкретном их количестве задается в mirror-конфигурации BootInfo.

Формат файла загружаемого образа, где разрешены настройки encryption & signature:

BL602 boot image encryption signature fig02

Рис. 2. UART/SDIO boot image, с шифрованием и цифровой подписью (encrypted & signed).

В сравнении с обычными загружаемыми образами, в образах "encrypted & signed" область BootInfo должна включать такую информацию, как публичные ключи (public keys), цифровые подписи (signatures), и AES IV.

Формат файла загружаемого образа, где разрешены настройки encryption, но без подписи:

BL602 boot image encryption no signature fig03

Рис. 3. UART/SDIO boot image, с шифрованием, но без цифровой подписи (encrypted & no signature).

Формат файла загружаемого образа, где разрешена подпись, но не применено шифрование:

BL602 boot image no encryption signature fig04

Рис. 4. UART/SDIO boot image, без шифрования, с цифровой подписью (unencrypted & signature).

[Выводы управления загрузкой]

BL602 для перепрошивки поддерживает один интерфейс UART и один интерфейс SDIO.

Таблица 1. Выводы чипа, используемые для загрузки UART/SDIO.

Порт GPIO Функция Примечание
GPIO8 Вход управления активацией BOOT ROM (лог. 1).  
GPIO7 BL602 UART RXD UART Channel1
GPIO16 BL602 UART TXD
GPIO0 SDIO_CLK SDIO Channel
GPIO1 SDIO_CMD
GPIO2 SDIO_DATA0
GPIO3 SDIO_DATA1
GPIO4 SDIO_DATA2
GPIO5 SDIO_DATA3

Если вы хотите запустить загрузку через UART/SDIO, то нужно подать на ножку порта GPIO8 подать лог. 1, и затем сбросить чип. Код Bootrom будет сканировать по очереди интерфейсы UART и SDIO, и ждать на них сигнала квитирования "handshake signal". По истечению handshake timeout (2 мс) происходит переход к следующему интерфейсу. Если на интерфейсе прошел успешный handshake, то произойдет вход в процесс приема данных. Во время обработки данных, если произошла ошибка передачи или таймаут (2 с), то произойдет сканирование следующего интерфейса, и так далее по циклу, пока не будет принят правильный сигнал Start the mirror для завершения задачи startup.

[UART handshake]

Настройки для обмена данными через UART: 1 start bit, 8 data bit, 1 stop bit, no parity.

После запуска Bootrom периодически мониторит изменение уровня на входе вывода порта GPIO7. Когда хост посылает строку данных из байт 0x55 (утилита bflb-iot-tool [6] посылает 600 байт 0x55), эти данные захватываются и анализируются, и по ним код Bootrom вычисляет необходимую скорость обмена. По результатам этих вычислений Bootrom настраивает регистры UART. После этой настройки Bootrom выдает ответ "OK". Хост, получив этот ответ, может продолжить нормальный обмен. Время таймаута UART-коммуникации составляет 2 секунды. Если Bootrom не получит никаких данных в течение 2 с после выдачи "OK", или если в процессе обмена был перерыв 2 секунды, то Bootrom считает это ошибкой таймаута и заново входит в процесс обнаружения handshake.

Рекомендуемое время, в течение которого хост отправляет handshake-данные, составляет 5 мс, что должно быть достаточно, чтобы Bootrom нормально определил параметры сигнала handshake. После того, как хост получил "OK", рекомендуется выдержать задержку 20 мс перед продолжением обмена, чтобы последующие данные не смешивались с данными handshake. Поскольку Bootrom использует такты RC32M для загрузки через UART/SDIO, для handshake рекомендуется не превышать скорость 500 килобит/сек. Процесс handshake выглядит следующим образом:

BL602 UART handshake process fig05

Рис. 5. Алгоритм handshake.

Код Bootrom ждет SDIO-хост для записи handshake-регистра (SDU_BASE+0x160). Когда запрос SDIO обнаружит, что в handshake-регистр записана 1, процедура рукопожатия считается успешной. Код Bootrom будет ждать обмена со стороны хоста для передачи данных в соответствии с полученными командами. Если произошла пауза в обмене (не было получено никаких данных от хоста в течение 2 секунд), произойдет возврат на стадию ожидания handshake.

Образ прошивки генерируется командой make, пример (см. файл README.md):

$ make CHIP=bl602 BOARD=bl602dk

Компания Bouffalo предоставляет специальный инструментарий для генерации загружаемого образа прошивки UART/SDIO (download mirror generation tools).

[Windows]

Пользователи могут загрузить Bouffalo Lab Dev Cube For Windows [7], получить последнюю версию Dev Cube, запустить BLDevCube.exe, выбрать BL602/604 в поле Chip Type, и войти в интерфейс программирования. Выберите опцию MCU в меню View, чтобы войти в интерфейс загрузки программы (MCU program download).

BLFlashCube Windows main screen fig06

Рис. 6. Интерфейс пользователя утилиты tools/bflb_tools/bouffalo_flash_cube/BLFlashCube.exe.

Если генерируется только загружаемый образ UART/SDIO, то можно правильно сконфигурировать только параметры программирования:

• Boot Source: выбирается UART/SDIO, что означает генерацию образа загрузки UART/SDIO (boot image).
• BootInfo Addr: адрес сохранения в памяти flash, здесь обычно указывается 0x0.
• Image Type: по умолчанию это SingleCPU.
• Image Addr: адрес загрузки программы приложения, пользователь может заполнить это поле адресом реально запускаемого кода, таким как 0x22020800.
• Image File: выбор программы RAM, скомпилированной и сгенерированной пользователем.

После завершения конфигурирования опций кликните на кнопку Create&Download для генерации соответствующего файла образа. Путь до генерируемого файла: bl602/img_create2/img_if.bin. Здесь img_if.bin это файл, который удовлетворяет формату UART/SDIO boot image.

Чтобы разрешить функционал encryption и signature, раскройте раздел advanced options, и после завершения конфигурирования также кликните на кнопку Create&Download.

[Linux]

BLFlashCube-ubuntu. Утилита для Linux, аналогичная BLFlashCube.exe:

BLFlashCube ubuntu main screen fig07

Рис. 7. Главный экран Linux-версии BLFlashCube (tools/bflb_tools/bouffalo_flash_cube/BLFlashCube-ubuntu).

Замечание: для запуска BLFlashCube-ubuntu могут понадобиться права sudo.

BLFlashCommand-ubuntu. Утилита командной строки для прошивки микроконтроллеров Bouffalo. Пример запуска для прошивки чипа BL602:

$ ./../bouffalo_sdk/tools/bflb_tools/bouffalo_flash_cube/BLFlashCommand-ubuntu --interface=uart \
 --baudrate=2000000 --port=/dev/ttyACM0 --chipname=bl602 --cpu_id= --config=flash_prog_cfg.ini

В этом примере команда запускается из корневого каталога проекта, в котором находится файл конфигурации flash_prog_cfg.ini (например, проект из SDK bouf_hello_world). Назначение опций очевидно и не требует специального описания. Пример содержимого файла flash_prog_cfg.ini:

[cfg]
# 0: без стирания, 1: стираются программируемые секции, 2: chip erase
erase = 1
# skip mode, здесь настраиваются пары адрес, длина. Пары нескольких
# сегментов разделяются ;
skip_mode = 0x0, 0x0
# 0: не использовать isp mode, 1: isp mode
boot2_isp_mode = 0
 
[FW]
# Имя файла прошивки (build/build_out/helloworld_bl602.bin):
filedir = ./build/build_out/helloworld_$(CHIPNAME).bin
# Начальный адрес:address = 0x000000

bflb-iot-tool. Это еще одна утилита командной строки, написанная на Python [6]. По исходному коду можно самому разобраться в протоколе прошивки, и прошивать чипы Biuffalo не только утилитами PC, но и другим микроконтроллером. Пример запуска:

$ bflb-iot-tool --chipname BL602 --interface uart --port /dev/ttyS5 --firmware \
/mnt/c/asm/bouf_hello_world/build/build_out/helloworld_bl602.bin --baudrate 230400 --addr 0

В этом примере утилита bflb-iot-tool запускалась из виртуальной машины WSL [8]. Установка утилиты проводилась в версии Python 3.6.15.

$ pip install bflb-iot-tool

[Протокол обмена UART/SDIO для загрузки программы]

После того, как Bootrom успешно завершил процесс UART/SDIO, он может войти в состояние обмена данными для загрузки программы. Описание этого процесса приведено ниже, в нем хостом называется утилита PC или управляющий микроконтроллер, которые передают прошивку, а именем BL602 называется программируемый микроконтроллер Bouffalo. Следует заметить, что максимальная длина блока данных протокола Bootrom составляет 4096 байт.

Get boot info. Для получения информации загрузки хост выдает следующую команду.

Таблица 2. Хост -> BL602.

cmdId (1 байт) Rsvd (1 байт) Len_lsb (1 байт) Len_msb (1 байт)
0x10 0x00 0x00 0x00

Ответ BL602:

Таблица 3. BL602 -> хост.

'OK' (2 байта) Len_lsb (1 байт) Len_msb (1 байт) BootRom Version (4 байта) OTP info (16 байт)
0x4F 0x4B 0x14 0x00    

Это первая команда, с которой начинается обмен хоста с BL602, по ней хост получает информацию, относящуюся к BL602. Хост должен решить, требуется ли для BL602 подписанный образ, ориентируясь на значение sign_type. В соответствии с encrypted, хост также определяет, требует ли BL602 зашифрованный образ. Если известно, что чип не запускается с шифрованием и подписью, то анализ этой информации может быть пропущен.

Таблица 4. Назначение опций типа подписи (sign_type) и шифрования (encrypted).

  2 бита 00 Другое
sign_type Нет подписи Подпись
encrypted Нет шифрования Шифрование

Load boot header.

Таблица 5. Хост -> BL602.

cmdId (1 байт) Rsvd (1 байт) Len_lsb (1 байт) Len_msb (1 байт) BootHeader (176 байт)
0x11 0x00 0xb0 0x00  

Таблица 6. BL602 -> Хост.

'OK' (2 байта)
0x4F 0x4B

Структура заголовка загрузки (BootHeader) из 176 байт:

__PACKED_STRUCT boot_flash_cfg_t
{
   uint32_t magiccode;
   /*'FCFG'*/
   SPI_Flash_Cfg_Type cfg;
   uint32_t crc32;
};
 
__PACKED_STRUCT sys_clk_cfg_t
{
   uint8_t xtal_type;
   uint8_t pll_clk;
   uint8_t hclk_div;
   uint8_t bclk_div;
   uint8_t flash_clk_type;
   uint8_t flash_clk_div;
   uint8_t rsvd[2];
};
 
__PACKED_STRUCT boot_clk_cfg_t
{
   uint32_t magiccode;          /*'PCFG'*/
   struct sys_clk_cfg_t cfg;
   uint32_t crc32;
};
 
__PACKED_STRUCT bootheader_t
{
   uint32_t magiccode;          /*'BFXP'*/
   uint32_t revison;
   struct boot_flash_cfg_t flashCfg;
   struct boot_clk_cfg_t clkCfg;
   __PACKED_UNION {
      __PACKED_STRUCT {
         uint32_t sign:2;                /* [1: 0] для подписи */
         uint32_t encrypt_type:2;        /* [3: 2] для шифрования */
         uint32_t key_sel:2;             /* [5: 4] для выбора ключа в интерфейсе boot */
         uint32_t rsvd6_7:2;             /* [7: 6] для шифрования */
         uint32_t no_segment:1;          /* [8]    нет информации сегмента */
         uint32_t cache_enable:1;        /* [9]    для кэша */
         uint32_t notload_in_bootrom :1; /* [10]   не загружать этот образ в bootrom */
         uint32_t aes_region_lock:1;     /* [11]   aes region lock */
         uint32_t cache_way_disable:4;   /* [15: 12] cache way disable info */
         uint32_t crc_ignore:1;          /* [16]   игнорировать crc */
         uint32_t hash_ignore:1;         /* [17]   игнорировать хэш */
         uint32_t halt_ap:1;             /* [18]   остановить приложение */
         uint32_t rsvd19_31:13;          /* [31:19] зарезервировано */
      } bval;
      uint32_t wval;
   }bootcfg;
 
   uint32_t segment_cnt;
   uint32_t bootentry;   /* точка входа образа */
   uint32_t flashoffset;
 
   uint8_t hash[BFLB_BOOTROM_HASH_SIZE];  /* хэш образа */
   uint32_t rsv1;
   uint32_t rsv2;
   uint32_t crc32;
};

Таблица 7. Хост -> BL602.

cmdId (1 байт) Rsvd (1 байт) Len_lsb (1 байт) Len_msb (1 байт) PKey (68 байт)
0x12 0x00 0x44 0x00  

Таблица 8. BL602 -> Хост.

'OK' (2 байта)
0x4F 0x4B

Хост посылает эту команду только когда образ подписан. Если подписи нет, то отправка этой команды должна быть пропущена. 68-байтная структура публичного ключа следующая:

__PACKED_STRUCT pkey_cfg_t
{
   uint8_t eckeyx[32];   //ec key in boot info
   uint8_t eckeyy[32];   //ec key in boot info
   uint32_t crc32;
};

Таблица 9. Хост -> BL602.

cmdId (1 байт) Rsvd (1 байт) Len_lsb (1 байт) Len_msb (1 байт) Signature (N байт)
0x14 0x00 N & 0xFF (N & 0xFF00) >> 8  

Таблица 10. BL602 -> Хост.

'OK' (2 байта)
0x4F 0x4B

Хост посылает эту команду только когда образ подписан. Если подписи нет, то отправка этой команды должна быть пропущена. 68-байтная структура публичного ключа следующая:

Эффективная длина подписи (signature) не фиксирована, и её структура следующая:

__PACKED_STRUCT sign_cfg_t
{
   uint32_t sig_len;
   uint8_t signature[sig_len];
   uint32_t crc32;
};

Когда хост посылает сигнатуру, он может сначала прочитать sig_len, чтобы получить длину сигнатуры для отправки как sig_len+8.

Таблица 11. Хост -> BL602.

cmdId (1 байт) Rsvd (1 байт) Len_lsb (1 байт) Len_msb (1 байт) AES IV (20 байт)
0x16 0x00 0x14 0x00  

Таблица 12. BL602 -> Хост.

'OK' (2 байта)
0x4F 0x4B

Хост посылает эту команду только когда образ зашифрован. Если шифрования нет, то отправка этой команды должна быть пропущена. 68-байтная структура публичного ключа следующая:

20-байтная структура AES IV:

__PACKED_STRUCT aesiv_cfg_t
{
   uint8_t aesiv[16];
   uint32_t crc32;
};

Load Segment Header.

Таблица 13. Хост -> BL602.

cmdId (1 байт) Rsvd (1 байт) Len_lsb (1 байт) Len_msb (1 байт) Заголовок сегмента (16 байт)
0x17 0x00 0x10 0x00  

Таблица 14. BL602 -> Хост.

'OK' (2 байта)
0x4F 0x4B

Образ загрузки UART/SDIO (boot image) поддерживает несколько сегментов, и данные и код каждого сегмента может быть загружен программой загрузчика по адресу, указанному заголовком сегмента (Segment Header). Количество сегментов в образе определяется полем segment_cnt структуры bootheader_t (см. выше "Load boot header"). Хост должен записать эту переменную во время обработки boot header, и затем организовать цикл загрузки заголовка сегмента (Load Segment Header) и данных сегмента (Load Segment Data) в количестве segment_cnt итераций.

16-байтный заголовок сегмента описывается следующей структурой:

__PACKED_STRUCT segment_header_t
{
   uint32_t destaddr;
   uint32_t len;
   uint32_t rsvd;
   uint32_t crc32;
};

Load Segment Data.

Таблица 13. Хост -> BL602.

cmdId (1 байт) Rsvd (1 байт) Len_lsb (1 байт) Len_msb (1 байт) Signature (N байт)
0x18 0x00 N & 0xFF (N & 0xFF00) >> 8  

Таблица 14. BL602 -> Хост.

'OK' (2 байта)
0x4F 0x4B

Для данных сегмента из-за того, что есть ограничение в 4096 на кадр протокола, может потребоваться послать Load Segment Data несколько раз. Необходимо гарантировать, чтобы сумма длин фреймов данных, переданных несколько раз, была равна длине, указанной в заголовке сегмента Segment Header.

Check image. После того, как образ был загружен в RAM, он должен быть проверен на целостность и корректность, для этого используется соответствующая команда.

Таблица 15. Хост -> BL602.

cmdId (1 байт) Rsvd (1 байт) Len_lsb (1 байт) Len_msb (1 байт)
0x19 0x00 0x00 0x00

Таблица 16. BL602 -> Хост.

'OK' (2 байта)
0x4F 0x4B

Run image. Когда команда проверки образа получила ответ OK, загруженный в RAM образ может быть запущен командой запуска образа. После того как BL602 выполнит эту команду, управление перейдет из программы загрузчика (UART/SDIO startup program) к программе в загруженном образе.

Таблица 17. Хост -> BL602.

cmdId (1 байт) Rsvd (1 байт) Len_lsb (1 байт) Len_msb (1 байт)
0x1A 0x00 0x00 0x00

Таблица 18. BL602 -> Хост.

'OK' (2 байта)
0x4F 0x4B

[Error response frame]

Все описанные выше фреймы протокола соответствуют безошибочному обмену данными. Однако если в обмене были обнаружены ошибки, используется следующий формат ошибки в возвращаемых данных, и пользователь может запросить причину ошибки в соответствии с кодом ошибки (error code):

Таблица 19. BL602 -> Хост.

'FL' (2 байта) Error_Code_LSB (1 байт) Error_Code_MSB (1 байт)
0x46 0x4C    

Значения Error_Code:

typedef enum tag_bootrom_error_code_t
{
   BFLB_BOOTROM_SUCCESS                         = 0x00,
 
   /* flash */
   BFLB_BOOTROM_FLASH_INIT_ERROR                = 0x0001,
   BFLB_BOOTROM_FLASH_ERASE_PARA_ERROR          = 0x0002,
   BFLB_BOOTROM_FLASH_ERASE_ERROR               = 0x0003,
   BFLB_BOOTROM_FLASH_WRITE_PARA_ERROR          = 0x0004,
   BFLB_BOOTROM_FLASH_WRITE_ADDR_ERROR          = 0x0005,
   BFLB_BOOTROM_FLASH_WRITE_ERROR               = 0x0006,
   BFLB_BOOTROM_FLASH_BOOT_PARA                 = 0x0007,
 
   /* cmd */
   BFLB_BOOTROM_CMD_ID_ERROR                    = 0x0101,
   BFLB_BOOTROM_CMD_LEN_ERROR                   = 0x0102,
   BFLB_BOOTROM_CMD_CRC_ERROR                   = 0x0103,
   BFLB_BOOTROM_CMD_SEQ_ERROR                   = 0x0104,
 
   /* image */
   BFLB_BOOTROM_IMG_BOOTHEADER_LEN_ERROR        = 0x0201,
   BFLB_BOOTROM_IMG_BOOTHEADER_NOT_LOAD_ERROR   = 0x0202,
   BFLB_BOOTROM_IMG_BOOTHEADER_MAGIC_ERROR      = 0x0203,
   BFLB_BOOTROM_IMG_BOOTHEADER_CRC_ERROR        = 0x0204,
   BFLB_BOOTROM_IMG_BOOTHEADER_ENCRYPT_NOTFIT   = 0x0205,
   BFLB_BOOTROM_IMG_BOOTHEADER_SIGN_NOTFIT      = 0x0206,
   BFLB_BOOTROM_IMG_SEGMENT_CNT_ERROR           = 0x0207,
   BFLB_BOOTROM_IMG_AES_IV_LEN_ERROR            = 0x0208,
   BFLB_BOOTROM_IMG_AES_IV_CRC_ERROR            = 0x0209,
   BFLB_BOOTROM_IMG_PK_LEN_ERROR                = 0x020a,
   BFLB_BOOTROM_IMG_PK_CRC_ERROR                = 0x020b,
   BFLB_BOOTROM_IMG_PK_HASH_ERROR               = 0x020c,
   BFLB_BOOTROM_IMG_SIGNATURE_LEN_ERROR         = 0x020d,
   BFLB_BOOTROM_IMG_SIGNATURE_CRC_ERROR         = 0x020e,
   BFLB_BOOTROM_IMG_SECTIONHEADER_LEN_ERROR     = 0x020f,
   BFLB_BOOTROM_IMG_SECTIONHEADER_CRC_ERROR     = 0x0210,
   BFLB_BOOTROM_IMG_SECTIONHEADER_DST_ERROR     = 0x0211,
   BFLB_BOOTROM_IMG_SECTIONDATA_LEN_ERROR       = 0x0212,
   BFLB_BOOTROM_IMG_SECTIONDATA_DEC_ERROR       = 0x0213,
   BFLB_BOOTROM_IMG_SECTIONDATA_TLEN_ERROR      = 0x0214,
   BFLB_BOOTROM_IMG_SECTIONDATA_CRC_ERROR       = 0x0215,
   BFLB_BOOTROM_IMG_HALFBAKED_ERROR             = 0x0216,
   BFLB_BOOTROM_IMG_HASH_ERROR                  = 0x0217,
   BFLB_BOOTROM_IMG_SIGN_PARSE_ERROR            = 0x0218,
   BFLB_BOOTROM_IMG_SIGN_ERROR                  = 0x0219,
   BFLB_BOOTROM_IMG_DEC_ERROR                   = 0x021a,
   BFLB_BOOTROM_IMG_ALL_INVALID_ERROR           = 0x021b,
 
   /* IF */
   BFLB_BOOTROM_IF_RATE_LEN_ERROR               = 0x0301,
   BFLB_BOOTROM_IF_RATE_PARA_ERROR              = 0x0302,
   BFLB_BOOTROM_IF_PASSWORDERROR                = 0x0303,
   BFLB_BOOTROM_IF_PASSWORDCLOSE                = 0x0304,
 
   /* разное */
   BFLB_BOOTROM_PLL_ERROR                       = 0xfffc,
   BFLB_BOOTROM_INVASION_ERROR                  = 0xfffd,
   BFLB_BOOTROM_POLLING                         = 0xfffe,
   BFLB_BOOTROM_FAIL                            = 0xffff,
}bootrom_error_code_t;

[Алгоритм процесса загрузки]

Для программы, которая не требует шифрования и цифровой подписи, и когда в ней только один сегмент, процесс загрузки можно описать следующим образом (на примере чипа BL602, загружаемого через UART):

1. BL602 настраивается для загрузки из UART/SDIO.

2. Открывается последовательный порт, устанавливается скорость обмена, и открывается загружаемый файл.

fp = open("img_if.bin","rb");

3. Посылается блок данных синхронизации из байт 0x55 (handshake signal).

UART_Send (handshake55, 600);

4. Ожидание получения ответа OK, задержка 20 мс.

5. Отправка команды запроса информации загрузки (Get boot info).

6. Ожидание получения 4+20 байт ответа.

7. Чтение из файла 176 байт данных (data = fp.read(176);), используется команда загрузки заголовка (Load boot header) для отправки 176 байт структуры bootheader_t.

8. Ожидание приема ответа OK.

9. Чтение из файла 16 байт данных (data = fp.read(16)), парсинг общей длины данных сегмента, и использование команды загрузки заголовка сегмента (Load Segment Header) для отправки 16 байт структуры (segment_header_t).

10. Ожидание приема ответа OK.

11. Организация цикла для отправки данных сегмента:

sendDataLen=0;
while (sendDataLen < segDataLen)
{
   readDataLen = segDataLen - sendDataLen;
   if (readDataLen > 4096-4)
   {
      readDataLen=4096-4;
      // Чтение readDataLen байт данных:
      data = fp.read(readDataLen);
      // Использование команды Load Segment Data для отправки readDataLen байт.
      ...
      sendDataLen += readDataLen;
     // Ожидание ответа OK.
   }
}

12. Отправка команды проверки образа (Check image), ожидание получения ответа OK.

13. Отправка команды запуска образа (Run image), ожидание получения ответа OK.

Если во время описанного выше процесса код Bootrom возвратит ошибку, то процесс загрузки будет прерван.

[Eflash_loader]

Eflash_loader это исполняемая программа микроконтроллера для программирования, чтения и проверки Flash. Она может быть загружена в RAM и запущена через UART/SDIO. Образ Eflash_Loader не зашифрован и не подписан, и у него только один сегмент. Структура образа Eflash_loader:

BL602 Eflash Loader image fig08

Рис. 8. Образ Eflash_Loader.

С помощью выполнения шагов 1 .. 13, описанных выше в разделе "Алгоритм процесса загрузки" вы можете загрузить и запустить образ Eflash_loader.

Образы RAM-загрузчика. Двоичные файлы образов Eflash_Loader (*.bin) можно найти в Bouffalo SDK, в каталоге утилиты прошивальщика. Например, для чипа BL602 это может быть папка наподобие tools/bflb_tools/bouffalo_flash_cube/chips/bl602/eflash_loader/:

$ ls -l ~/bouffalo_sdk/tools/bflb_tools/bouffalo_flash_cube/chips/bl602/eflash_loader/
total 2408
-rwxrwxrwx 1 999 999   37376 авг  7 12:55 eflash_loader_24m.bin
-rwxrwxrwx 1 999 999   37376 авг  7 12:55 eflash_loader_26m.bin
-rwxrwxrwx 1 999 999   37376 авг  7 12:55 eflash_loader_32m.bin
-rwxrwxrwx 1 999 999   37376 авг  7 12:55 eflash_loader_38p4m.bin
-rwxrwxrwx 1 999 999   37376 авг  7 12:55 eflash_loader_40m.bin
-rwxrwxrwx 1 999 999    2010 авг  7 12:55 eflash_loader_cfg.conf
-rwxrwxrwx 1 999 999    1888 дек  4 18:05 eflash_loader_cfg.ini
-rwxrwxrwx 1 999 999 1296644 авг  7 12:55 eflash_loader.elf
-rwxrwxrwx 1 999 999  952627 авг  7 12:55 eflash_loader.map

Обратите внимание, что в именах файлов закодирована тактовая частота, с которой работает чип. Т. е. eflash_loader_24m.bin соответствует частоте кварца 24 МГц, eflash_loader_26m.bin частоте кварца 26 МГц, и так далее.

Протокол обмена Eflash_loader. После того, как хост через UART/SDIO загрузил в RAM и запустил образ Eflash_loader, хост продолжает обмен через UART с программой Eflash_loader. Используемые выводы те же самые (см. таблицу 1), и процесс рукопожатия (handshake) такой же, как был описан выше в разделе "UART handshake". Eflash_loader запускается в режиме высокочастотного тактирования с помощью точно настроенной системы PLL, что делает возможным использование повышенных скоростей обмена для handshake. Рекомендуемые скорости обмена 115200, 1M, 2M, 2.5M.

Рукопожатие RAM-загрузчика (handshake) представляет собой непрерывную последовательность из байт 0x55. Порт настраивается на 1 start-бит и на 1 stop-бит, благодаря чему получается четкий меандр длительностью 6 мс. По этому меандру загрузчик автоматически определяет скорость дальнейшей передачи, и в случае успеха выдает сообщение подтверждения "OK". Сообщение "OK" сигнализирует о готовности RAM-загрузчика работать на новой скорости. Если сообщение "OK" не поступило, то может быть выполнено несколько дополнительных попыток выдать handshake на такой же скорости или на других скоростях.

Загрузчик позволяет определять ряд скоростей 115200, 230400, 500000, 1000000, 2000000, 2500000, 3000000 бод. Независимо от скорости обмена последовательность handshake всегда имеет длительность 6 мс, и загрузчик отвечает сообщением "OK" в течение примерно 10 мс.

После успешного завершения handshake, хост реализует программирование памяти Flash следующими командами протокола.

Таблица 20. Хост -> BL602.

cmdId (1 байт) cksum (1 байт) Len_lsb (1 байт) Len_msb (1 байт)
0x3C Контрольная сумма для длины Len 0x00 0x00

Таблица 21. BL602 -> Хост.

'OK' (2 байта)
0x4F 0x4B

Эта команда выполняет стирание всей памяти flash. Данные, участвующие в вычислении контрольной суммы, это все данные после байтов cksum (следующие инструкции одинаковы). Проверка необязательна. Если вы не хотите включать проверку, вы можете установить cksum равным 0.

Предположим, что длина данных, участвующих в вычислении контрольной суммы, равна data_len (включая младший и старший байты длины Len_lsb и Len_msb). Тогда псевдокод для вычисления контрольной суммы будет следующий:

uint32_t sum = 0;
uint8_t cksum;
 
while(i < data_len)
{
   sum+=data[i];
   i++;
}
chsum = sum & 0xff;

Таблица 22. Хост -> BL602.

cmdId (1 байт) cksum (1 байт) Len_lsb (1 байт) Len_msb (1 байт) Start_addr (4 байта) End_addr (4 байта)
0x30 Контрольная сумма данных 0x08 0x00    

Таблица 23. BL602 -> Хост.

'OK' (2 байта)
0x4F 0x4B

Эта команда используется для очистки определенных областей Flash по указанному адресу.

Память Flash начинается с адреса 0. Ниже для примера показано адресное пространство 1M Flash:

BL602 Flash address space fig09

Рис. 9. Память BL602.

Таблица 24. Хост -> BL602.

cmdId (1 байт) cksum (1 байт) Len_lsb (1 байт) Len_msb (1 байт) Start_addr (4 байта) End_addr (4 байта)
0x31 Контрольная сумма данных (N+4) & 0xff ((N+4) >> 8) & 0xff    

Таблица 25. BL602 -> Хост.

'OK' (2 байта)
0x4F 0x4B

Запишет Nbytes байт данных по указанному адресу Flash. Из-за ограничения на размер буфера, используемого в Eflash_loader, максимальная полезная нагрузка этой команды ограничена 8 килобайтами.

Таблица 26. Хост -> BL602.

cmdId (1 байт) cksum (1 байт) Len_lsb (1 байт) Len_msb (1 байт)
0x3A Контрольная сумма для длины Len 0x00 0x00

Таблица 27. BL602 -> Хост.

'OK' (2 байта)
0x4F 0x4B

Эта команда используется для проверки, была ли ошибка в процессе программирования данных flash, после того как все программируемые данные были переданы. Если программирование Flash было корректным, то будет возвращен ответ OK. Иначе будет ответ FL+ error code, где error code равен BFLB_EFLASH_LOADER_FLASH_WRITE_ERROR, подробнее см. описание фрейма ответа ошибки (error response frame).

Таблица 28. Хост -> BL602.

cmdId (1 байт) cksum (1 байт) Len_lsb (1 байт) Len_msb (1 байт) Start_addr (4 байта) Read_len (4 байта)
0x32 Контрольная сумма данных 0x08 0x00    

Таблица 29. BL602 -> Хост.

'OK' (2 байта) Len_lsb (1 байт) Len_msb (1 байт) Полезная нагрузка (N байт)
0x4F 0x4B N & 0xff (N>>8) & 0xff  

Эта команда вычитывает Nbytes байт данных по указанному адресу Flash. Из-за ограничения на размер буфера Eflash_loader максимальный размер блока считываемых данных Read_len составляет 8K.

Таблица 30. Хост -> BL602.

cmdId (1 байт) cksum (1 байт) Len_lsb (1 байт) Len_msb (1 байт) Start_addr (4 байта) Len (4 байта)
0x3D Контрольная сумма данных 0x08 0x00    

Таблица 31. BL602 -> Хост.

'OK' (2 байта) Len_lsb (1 байт) Len_msb (1 байт) Полезная нагрузка (32 байта)
0x4F 0x4B 0x20 0x00  

Эта команда используется для быстрой проверки корректности программирования Flash. Хост посылает начальный адрес данных flash, по которым вычисляется хэш, и BL602 вернет соответствующее значение SHA256. Хост также синхронно вычисляет SHA256 от только что прошитого файла, и затем сравнивает свою SHA256 и SHA256 возвращенного результата, чтобы определить, что память Flash была корректно запрограммирована.

Описанные выше фреймы обмена BL602 и хоста соответствуют ситуациям, когда ошибки отсутствуют. В случае ошибки Eflash_loader возвратит следующий фрейм ошибки:

Таблица 32. BL602 -> Хост.

'FL' (2 байта) Error_Code_LSB (1 байт) Error_Code_MSB (1 байт)
0x46 0x4C    

Коды ошибки Error_Code:

typedef enum tag_eflash_loader_error_code_t
{
   BFLB_EFLASH_LOADER_SUCCESS                       = 0x00,
 
   /*flash*/
   BFLB_EFLASH_LOADER_FLASH_INIT_ERROR              = 0x0001,
   BFLB_EFLASH_LOADER_FLASH_ERASE_PARA_ERROR        = 0x0002,
   BFLB_EFLASH_LOADER_FLASH_ERASE_ERROR             = 0x0003,
   BFLB_EFLASH_LOADER_FLASH_WRITE_PARA_ERROR        = 0x0004,
   BFLB_EFLASH_LOADER_FLASH_WRITE_ADDR_ERROR        = 0x0005,
   BFLB_EFLASH_LOADER_FLASH_WRITE_ERROR             = 0x0006,
   BFLB_EFLASH_LOADER_FLASH_BOOT_PARA_ERROR         = 0x0007,
   BFLB_EFLASH_LOADER_FLASH_SET_PARA_ERROR          = 0x0008,
   BFLB_EFLASH_LOADER_FLASH_READ_STATUS_REG_ERROR   = 0x0009,
   BFLB_EFLASH_LOADER_FLASH_WRITE_STATUS_REG_ERROR  = 0x000A,
 
   /*cmd*/
   BFLB_EFLASH_LOADER_CMD_ID_ERROR                  = 0x0101,
   BFLB_EFLASH_LOADER_CMD_LEN_ERROR                 = 0x0102,
   BFLB_EFLASH_LOADER_CMD_CRC_ERROR                 = 0x0103,
   BFLB_EFLASH_LOADER_CMD_SEQ_ERROR                 = 0x0104,
 
   /*image*/
   BFLB_EFLASH_LOADER_IMG_BOOTHEADER_LEN_ERROR      = 0x0201,
   BFLB_EFLASH_LOADER_IMG_BOOTHEADER_NOT_LOAD_ERROR = 0x0202,
   BFLB_EFLASH_LOADER_IMG_BOOTHEADER_MAGIC_ERROR    = 0x0203,
   BFLB_EFLASH_LOADER_IMG_BOOTHEADER_CRC_ERROR      = 0x0204,
   BFLB_EFLASH_LOADER_IMG_BOOTHEADER_ENCRYPT_NOTFIT = 0x0205,
   BFLB_EFLASH_LOADER_IMG_BOOTHEADER_SIGN_NOTFIT    = 0x0206,
   BFLB_EFLASH_LOADER_IMG_SEGMENT_CNT_ERROR         = 0x0207,
   BFLB_EFLASH_LOADER_IMG_AES_IV_LEN_ERROR          = 0x0208,
   BFLB_EFLASH_LOADER_IMG_AES_IV_CRC_ERROR          = 0x0209,
   BFLB_EFLASH_LOADER_IMG_PK_LEN_ERROR              = 0x020a,
   BFLB_EFLASH_LOADER_IMG_PK_CRC_ERROR              = 0x020b,
   BFLB_EFLASH_LOADER_IMG_PK_HASH_ERROR             = 0x020c,
   BFLB_EFLASH_LOADER_IMG_SIGNATURE_LEN_ERROR       = 0x020d,
   BFLB_EFLASH_LOADER_IMG_SIGNATURE_CRC_ERROR       = 0x020e,
   BFLB_EFLASH_LOADER_IMG_SECTIONHEADER_LEN_ERROR   = 0x020f,
   BFLB_EFLASH_LOADER_IMG_SECTIONHEADER_CRC_ERROR   = 0x0210,
   BFLB_EFLASH_LOADER_IMG_SECTIONHEADER_DST_ERROR   = 0x0211,
   BFLB_EFLASH_LOADER_IMG_SECTIONDATA_LEN_ERROR     = 0x0212,
   BFLB_EFLASH_LOADER_IMG_SECTIONDATA_DEC_ERROR     = 0x0213,
   BFLB_EFLASH_LOADER_IMG_SECTIONDATA_TLEN_ERROR    = 0x0214,
   BFLB_EFLASH_LOADER_IMG_SECTIONDATA_CRC_ERROR     = 0x0215,
   BFLB_EFLASH_LOADER_IMG_HALFBAKED_ERROR           = 0x0216,
   BFLB_EFLASH_LOADER_IMG_HASH_ERROR                = 0x0217,
   BFLB_EFLASH_LOADER_IMG_SIGN_PARSE_ERROR          = 0x0218,
   BFLB_EFLASH_LOADER_IMG_SIGN_ERROR                = 0x0219,
   BFLB_EFLASH_LOADER_IMG_DEC_ERROR                 = 0x021a,
   BFLB_EFLASH_LOADER_IMG_ALL_INVALID_ERROR         = 0x021b,
 
   /*IF*/
   BFLB_EFLASH_LOADER_IF_RATE_LEN_ERROR             = 0x0301,
   BFLB_EFLASH_LOADER_IF_RATE_PARA_ERROR            = 0x0302,
   BFLB_EFLASH_LOADER_IF_PASSWORDERROR              = 0x0303,
   BFLB_EFLASH_LOADER_IF_PASSWORDCLOSE              = 0x0304,
 
   /*MISC*/
   BFLB_EFLASH_LOADER_PLL_ERROR                     = 0xfffc,
   BFLB_EFLASH_LOADER_INVASION_ERROR                = 0xfffd,
   BFLB_EFLASH_LOADER_POLLING                       = 0xfffe,
   BFLB_EFLASH_LOADER_FAIL                          = 0xffff,
}eflash_loader_error_code_t;

Плохая новость - описание протокола BL602 ISP [1] фактически устаревшее, и в нем не приведены все команды Eflash_Loader, которые используются в настоящее время утилитами прошивки [6, 7].

Но есть и хорошая новость - благодаря тому, что есть исходный код утилиты bflb-iot-tool на Python, можно получить полный список команд Eflash_Loader из файла flb_iot_tool/libs/bflb_eflash_loader.py (см. class BflbEflashLoader, поле _com_cmds). Назначение почти всех команд можно угадать по их имени:

Имя команды Значение Длина данных
change_rate 0x20 8
reset 0x21 0
clk_set 0x22 0
opt_finish 0x23 0
flash_erase 0x30 0
flash_write 0x31 256
flash_read 0x32 256
flash_boot 0x33 0
flash_xip_read 0x34 256
flash_switch_bank 0x35 256
flash_read_jid 0x36 0
flash_read_status_reg 0x37 0
flash_write_status_reg 0x38 0
flash_write_check 0x3a 0
flash_set_para 0x3b 0
flash_chiperase 0x3c 0
flash_readSha 0x3d 256
flash_xip_readSha 0x3e 256
flash_decompress_write 0x3f 256
efuse_write 0x40 128
efuse_read 0x41 0
efuse_read_mac 0x42 0
efuse_write_mac 0x43 6
flash_xip_read_start 0x60 128
flash_xip_read_finish 0x61 0
log_read 0x71 0
efuse_security_write 0x80 128
efuse_security_read 0x81 0
ecdh_get_pk 0x90 0
ecdh_chanllenge 0x91 0

Этот сеанс обмена данными был снят с помощью утилиты Device Monitoring Studio [10] на ноутбуке под Windows 10. Прошивальщик bflb-iot-tool запускался на Ubintu 22.04 под виртуальной машиной WSL, работающей на том же ноутбуке, благодаря чему Device Monitoring Studio получила возможность обработать данные протокола.

Команда, лог обмен который был записан:

$ bflb_iot_tool --interface=uart --baudrate=2000000 --port=/dev/ttyUSB1 --chipname=bl602 \
 --addr=0x0 --firmware ~/bouf_hello_world/build/build_out/helloworld_bl602.bin
[09:28:25.261] - ==================================================
[09:28:25.261] - Chip name is bl602
[09:28:25.261] - Serial port is /dev/ttyUSB1
[09:28:25.261] - Baudrate is 2000000
[09:28:25.261] - Firmware is ~/bouf_hello_world/build/build_out/helloworld_bl602.bin
[09:28:25.261] - Partition Table is None
[09:28:25.261] - Device Tree is None
[09:28:25.261] - Boot2 is None
[09:28:25.261] - MFG is None
[09:28:25.261] - Media is None
[09:28:25.261] - Romfs Dir is None
[09:28:25.261] - ==================================================
[09:28:25.261] - ========= Interface is Uart =========
[09:28:25.266] - eflash loader bin is eflash_loader_40m.bin
[09:28:25.267] - ========= chip flash id: ef4015 =========
[09:28:25.268] - create partition.bin, pt_new is True
[09:28:25.271] - fw_boot_head_gen xtal: 40M
[09:28:25.272] - Create bootheader using ~/bflb_iot_tool/chips/bl602/img_create_iot/efuse_bootheader_cfg.ini
[09:28:25.272] - Updating data according to < ~/bflb_iot_tool/chips/bl602/img_create_iot/efuse_bootheader_cfg.ini
 [BOOTHEADER_CFG]>
[09:28:25.273] - Created file len:176
[09:28:25.274] - Create efuse using ~/bflb_iot_tool/chips/bl602/img_create_iot/efuse_bootheader_cfg.ini
[09:28:25.275] - Updating data according to < ~/bflb_iot_tool/chips/bl602/img_create_iot/efuse_bootheader_cfg.ini
 [EFUSE_CFG]>
[09:28:25.276] - Created file len:128
[09:28:25.278] - ========= sp image create =========
[09:28:25.278] - img already have bootheader
[09:28:25.278] - Image hash is b'58581c32dc0013b68f8ed789dc1f23a663242d824b4378c5fc0bae4d461f5a03'
[09:28:25.278] - Header crc: b'cf7e5ff6'
[09:28:25.278] - Write flash img
[09:28:25.279] - Encrypt efuse data
[09:28:25.281] - FW Header is 176, 3920 still needed
[09:28:25.281] - FW OTA bin header is Done. Len is 4096
[09:28:25.283] - BL60X_OTA_Ver1.0
[09:28:25.285] - FW OTA bin is Done. Len is 34896
[09:28:25.294] - OTA XZ file len = 18848
[09:28:25.294] - Partiton len = 557056
[09:28:25.294] - BL60X_OTA_Ver1.0
[09:28:25.296] - FW OTA xz is Done
[09:28:25.296] - ========= eflash loader config =========
[09:28:25.305] - Version: eflash_loader_v2.4.8
[09:28:25.305] - Program Start
[09:28:25.305] - ========= eflash loader cmd arguments =========
[09:28:25.305] - Config file: ~/bflb_iot_tool/chips/bl602/eflash_loader/eflash_loader_cfg.ini
[09:28:25.305] - serial port is /dev/ttyUSB1
[09:28:25.306] - cpu_reset=False
[09:28:25.306] - chiptype: bl602
[09:28:25.306] - ========= Interface is uart =========
[09:28:25.306] - com speed: 2000000
[09:28:25.306] - Eflash load helper file: ~/bflb_iot_tool/chips/bl602/eflash_loader/eflash_loader_40m.bin
[09:28:25.306] - ========= load eflash_loader.bin =========
[09:28:25.306] - Load eflash_loader.bin via uart
[09:28:25.306] - ========= image load =========
[09:28:25.507] - Not ack OK
[09:28:25.508] - FL
[09:28:25.508] - result: FL
[09:28:25.778] - tx rx and power off, press the machine!
[09:28:25.778] - cutoff time is 0.1
[09:28:25.879] - power on tx and rx
[09:28:26.605] - reset cnt: 0, reset hold: 0.005, shake hand delay: 0.1
[09:28:26.605] - clean buf
[09:28:26.605] - send sync
[09:28:26.809] - ack is 4f4b
[09:28:26.839] - shake hand success
[09:28:26.850] - get_boot_info
[09:28:26.859] - data read is b'010000000000000003000300175a894cb97c1800'
[09:28:26.860] - ========= chipid: 7cb94c895a17 =========
[09:28:26.860] - last boot info: None
[09:28:26.860] - sign is 0 encrypt is 0
[09:28:26.860] - segcnt is 1
[09:28:26.891] - segdata_len is 38464
[09:28:26.987] - 4080/38464
[09:28:27.082] - 8160/38464
[09:28:27.178] - 12240/38464
[09:28:27.274] - 16320/38464
[09:28:27.370] - 20400/38464
[09:28:27.466] - 24480/38464
[09:28:27.561] - 28560/38464
[09:28:27.657] - 32640/38464
[09:28:27.753] - 36720/38464
[09:28:27.801] - 38464/38464
[09:28:27.816] - Run img
[09:28:27.932] - Load helper bin time cost(ms): 2626.76123046875
[09:28:28.033] - Flash load shake hand
[09:28:28.050] - default set DTR high
[09:28:28.150] - clean buf
[09:28:28.150] - send sync
[09:28:28.357] - ack is 4f4b
[09:28:28.387] - Read mac addr
[09:28:28.390] - macaddr: 175a894cb97c
[09:28:28.390] - flash set para
[09:28:28.390] - ========= flash read jedec ID =========
[09:28:28.406] - Read flash jedec ID
[09:28:28.406] - flash jedec id: 5e401680
[09:28:28.406] - Finished
[09:28:28.413] - get flash size: 0x00400000
[09:28:28.413] - Program operation
[09:28:28.414] - Dealing Index 0
[09:28:28.414] - ========= programming chips/bl602/partition/partition.bin to 0x0000E000
[09:28:28.418] - ========= flash load =========
[09:28:28.418] - ========= flash erase =========
[09:28:28.418] - Erase flash from 0xe000 to 0xe10f
[09:28:28.422] - erase pending
[09:28:28.474] - Erase time cost(ms): 56.10009765625
[09:28:28.486] - Load 272/272 {"progress":100}
[09:28:28.486] - Load 272/272 {"progress":100}
[09:28:28.486] - Write check
[09:28:28.502] - Flash load time cost(ms): 27.48974609375
[09:28:28.502] - Finished
[09:28:28.502] - Sha caled by host: fd6af18fc4aaf2807277cac767ca19d12af7b55f5ecbb8902ef28bc2430524aa
[09:28:28.502] - xip mode Verify
[09:28:28.533] - Read Sha256/272
[09:28:28.533] - Flash xip readsha time cost(ms): 15.8564453125
[09:28:28.533] - Finished
[09:28:28.549] - Sha caled by dev: fd6af18fc4aaf2807277cac767ca19d12af7b55f5ecbb8902ef28bc2430524aa
[09:28:28.549] - Verify success
[09:28:28.549] - Dealing Index 1
[09:28:28.549] - ========= programming chips/bl602/partition/partition.bin to 0x0000F000
[09:28:28.553] - ========= flash load =========
[09:28:28.553] - ========= flash erase =========
[09:28:28.553] - Erase flash from 0xf000 to 0xf10f
[09:28:28.565] - erase pending
[09:28:28.617] - Erase time cost(ms): 63.7451171875
[09:28:28.629] - Load 272/272 {"progress":100}
[09:28:28.629] - Load 272/272 {"progress":100}
[09:28:28.629] - Write check
[09:28:28.645] - Flash load time cost(ms): 27.322998046875
[09:28:28.645] - Finished
[09:28:28.645] - Sha caled by host: fd6af18fc4aaf2807277cac767ca19d12af7b55f5ecbb8902ef28bc2430524aa
[09:28:28.645] - xip mode Verify
[09:28:28.677] - Read Sha256/272
[09:28:28.677] - Flash xip readsha time cost(ms): 16.40478515625
[09:28:28.677] - Finished
[09:28:28.693] - Sha caled by dev: fd6af18fc4aaf2807277cac767ca19d12af7b55f5ecbb8902ef28bc2430524aa
[09:28:28.693] - Verify success
[09:28:28.693] - Dealing Index 2
[09:28:28.693] - ========= programming ~/bflb_iot_tool/chips/bl602/img_create_iot/whole_img.bin to 0x00010000
[09:28:28.700] - ========= flash load =========
[09:28:28.700] - ========= flash erase =========
[09:28:28.700] - Erase flash from 0x10000 to 0x1884f
[09:28:28.708] - erase pending
[09:28:28.805] - erase pending
[09:28:28.856] - Erase time cost(ms): 156.324462890625
[09:28:28.856] - decompress write rx timeout: 49.152
[09:28:28.871] - decompress flash load 18848
[09:28:28.883] - Load 2048/18848 {"progress":10}
[09:28:28.899] - Load 4096/18848 {"progress":21}
[09:28:28.916] - Load 6144/18848 {"progress":32}
[09:28:28.931] - Load 8192/18848 {"progress":43}
[09:28:28.947] - Load 10240/18848 {"progress":54}
[09:28:28.963] - Load 12288/18848 {"progress":65}
[09:28:28.979] - Load 14336/18848 {"progress":76}
[09:28:28.995] - Load 16384/18848 {"progress":86}
[09:28:29.011] - Load 18432/18848 {"progress":97}
[09:28:29.027] - Load 18848/18848 {"progress":100}
[09:28:29.027] - Load 18848/18848 {"progress":100}
[09:28:29.027] - Write check
[09:28:29.043] - Flash load time cost(ms): 186.778076171875
[09:28:29.043] - Finished
[09:28:29.044] - Sha caled by host: c7fa07578672d5c4228b613db7850dfcc3c30d8ca9ffe47648ee00b96e6f326b
[09:28:29.044] - xip mode Verify
[09:28:29.075] - Read Sha256/34896
[09:28:29.075] - Flash xip readsha time cost(ms): 15.84326171875
[09:28:29.075] - Finished
[09:28:29.091] - Sha caled by dev: c7fa07578672d5c4228b613db7850dfcc3c30d8ca9ffe47648ee00b96e6f326b
[09:28:29.091] - Verify success
[09:28:29.091] - Program Finished
[09:28:29.091] - All time cost(ms): 3785.9296875
[09:28:29.197] - close interface
[09:28:29.197] - [All Success]

Замечание: можно также запустить код Python следующей командой:

$ python bflb_iot_tool/__main__.py --interface=uart --baudrate=2000000 --port=/dev/ttyUSB1 --chipname=bl602 \
 --addr=0x0 --firmware ~/bouf_hello_world/build/build_out/helloworld_bl602.bin

Обмен происходит строго по сценарию запрос от bflb-iot-tool - ответ от BL602 (синим цветом выделены данные bflb-iot-tool, красным цветом ответ от BL602). Начинается сеанс с выдачи пакета рукопожатия:

55 55 55 55 .. 55 300 байт 0x55. 

4F 4B OK BL602 засинхронизировался, установил соответствующую скорость передачи, и выдал соответствующее подтверждение.

10 00 00 00 Команда "Get boot info" (0x10).

4F 4B 14 00 01 00 00 00 00 00 00 00 03 00 04 00 E9 6E D9 10 17 A8 99 00 Ответ на команду "Get boot info": OK, и длина данных 0x0014. В данных содержится версия BootRom (01 00 00 00) и какая-то другая информация.

11 00 B0 00 42 46 4E 50 01 00 00 00 00 00 00 00 04 01 01 01 66 99 FF 03 9F 00 9F 00 04 EF 00 01 C7 20 52 D8 06 02 32 00 0B 01 0B 01 3B 01 BB 00 6B 01 EB 02 EB 02 02 50 00 01 00 01 01 00 02 01 01 01 AB 01 05 35 00 00 01 31 00 00 38 FF 20 FF 77 03 02 40 77 03 02 F0 2C 01 B0 04 B0 04 05 00 40 0D 03 00 48 D7 BD C4 00 00 00 00 04 04 00 01 02 00 00 00 BB 14 4E 82 00 03 03 00 01 00 00 00 00 00 00 00 00 00 01 22 EF BE AD DE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 EF BE AD DE Команда передачи заголовка (Load boot header, 0x11), длина передаваемых данных (0x00B0) и 176 байт заголовка.

4F 4B OK

17 00 10 00 00 00 01 22 40 96 00 00 B2 39 8F 43 3F 4A 9A 52 Команда передачи заголовка сегмента (Load Segment Header, 0x17), длина передаваемых данных (0x0010). Здесь содержится информация о передаваемом сегменте данных (см. структуру segment_header_t).

4F 4B 10 00 00 00 01 22 40 96 00 00 B2 39 8F 43 3F 4A 9A 52 Ответ на команду 0x17: OK и дублирование всех данных команды.

18 00 F0 0F 97 11 01 20 93 81 01 .. Команда передачи данных сегмента (Load Segment Data, 0x18), длина передаваемых данных 0x0FF0 (4080 байт) и передаваемые данные.

4F 4B OK

18 00 F0 0F EF 20 00 54 13 05 40 .. Команда передачи данных сегмента (Load Segment Data, 0x18), длина передаваемых данных 0x0FF0 (4080 байт) и передаваемые данные.

4F 4B OK

18 00 F0 0F EF 20 00 54 13 05 40 .. Команда передачи данных сегмента (Load Segment Data, 0x18), длина передаваемых данных (0x0FF0, 4080 байт) и передаваемые данные.

4F 4B OK

Процесс повторяется до последней порции данных передаваемого сегмента.

...

18 00 D0 06 EF DB 79 B7 A1 40 14 .. Это последняя порция данных передаваемого сегмента, их меньше (0x06D0, 1744 байта).

4F 4B OK

19 00 00 00 Команда проверки образа (Check image, 0x19).

4F 4B OK

1A 00 00 00 Команда запуска образа (Run image, 0x1A).

4F 4B OK

Сейчас загружена и получила управление программа нового загрузчика (Eflash_loader), которая теперь находится в памяти RAM, и она может прошивать Flash. Для синхронизации с ней снова запускается процедура handshake из последовательности байт 0x55, пока программа на ответит байтами 0x4F, 0x4B (OK). Байты handshake 0x55 могут передаваться сейчас на повышенной скорости (до 3 мегабит), потому что код загрузчика Eflash_loader работает из RAM на повышенной тактовой частоте, которая позволяет более точно формировать интервалы времени UART.

55 55 55 55 55 55 55 ..

4F 4B OK

42 00 00 00 Команда efuse_read_mac (0x42).

4F 4B 0A 00 E9 6E D9 10 17 A8 EA D8 7F 3C Ответ на команду: OK, длина данных 0x000A (10 байт) и сами данные.

36 00 00 00 Команда flash_read_jid (0x36).

4F 4B 04 00 C8 40 16 80 Ответ на команду: OK, длина данных 0x0004 (4 байта) и сами данные.

30 D8 08 00 00 E0 00 00 0F E1 00 00 Команда flash_erase (0x30), контрольная сумма данных 0xD8, длина данных 0x0008 (8 байт) и сами данные. В данных содержатся 2 адреса - адрес начала 0x0000E000 и адрес конца 0x0000E10F, т. е. должно быть очищено 0x0110 (272) байт. Замечание: контрольную сумму данных 0xD8 можно просто заменить нулевым байтом. Этой командой стирается область для таблицы разделов flash.

50 44 4F 4B PDOK, вроде как это подтверждение.

31 50 14 01 00 E0 00 00 42 46 50 54 00 00 07 00 00 00 00 00 26 9A DF 12 00 00 00 46 57 00 00 00 00 00 00 00 00 00 01 00 00 80 0E 00 00 80 0D 00 00 80 08 00 00 00 00 00 00 00 00 00 02 00 00 6D 66 67 00 00 00 00 00 00 00 00 17 00 00 00 00 00 00 20 03 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 6D 65 64 69 61 00 00 00 00 00 20 1A 00 00 00 00 00 00 70 04 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 50 53 4D 00 00 00 00 00 00 00 90 1E 00 00 00 00 00 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 4B 45 59 00 00 00 00 00 00 00 10 1F 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 00 00 44 41 54 41 00 00 00 00 00 00 30 1F 00 00 00 00 00 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 66 61 63 74 6F 72 79 00 00 00 80 1F 00 00 00 00 00 00 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 DE 2B 0C CE Команда flash_write (0x31). Здесь 0x50 контрольная сумма, 0x0114 длина данных (4 адрес назначения + 272 полезная записываемая нагрузка), адрес назначения 0x0000E000, и уже дальше полезная нагрузка. Этой командой записывается файл таблицы разделов (по умолчанию bflb_iot_tool/chips/bl602/partition/partition.bin).

4F 4B OK

3A 00 00 00 Команда flash_write_check (0x3A).

4F 4B OK

60 00 00 00 Команда flash_xip_read_start (0x60).

4F 4B OK

3E F9 08 00 00 E0 00 00 10 01 00 00 Команда flash_xip_readSha (0x3E). Опять догадываюсь: 0xF9 контрольная сумма, 0x0008 длина данных (8 байт). В данных наверное идут адрес начала 0x0000E000 и длина 0x000110.

4F 4B 20 00 FD 6A F1 8F C4 AA F2 80 72 77 CA C7 67 CA 19 D1 2A F7 B5 5F 5E CB B8 90 2E F2 8B C2 43 05 24 AA Ответ: OK, длина 0x0020 (32 байта), и далее идут 32 байта вычисленных данных хеша.

61 00 00 00 Команда flash_xip_read_finish (0x61). 

4F 4B OK

30 F8 08 00 00 F0 00 00 0F F1 00 00 Команда flash_erase (0x30). 0xF8 контрольная сумма, 0x0008 длина данных. В данных идут адрес начала 0x0000F000 и адрес конца 0x0000F10F. Т. е. стираются 0x110 (272) байта.

50 44 4F 4B PDOK

31 60 14 01 00 F0 00 00 42 46 50 54 00 00 07 00 00 00 00 00 26 9A DF 12 00 00 00 46 57 00 00 00 00 00 00 00 00 00 01 00 00 80 0E 00 00 80 0D 00 00 80 08 00 00 00 00 00 00 00 00 00 02 00 00 6D 66 67 00 00 00 00 00 00 00 00 17 00 00 00 00 00 00 20 03 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 6D 65 64 69 61 00 00 00 00 00 20 1A 00 00 00 00 00 00 70 04 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 50 53 4D 00 00 00 00 00 00 00 90 1E 00 00 00 00 00 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 4B 45 59 00 00 00 00 00 00 00 10 1F 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 00 00 44 41 54 41 00 00 00 00 00 00 30 1F 00 00 00 00 00 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 66 61 63 74 6F 72 79 00 00 00 80 1F 00 00 00 00 00 00 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 DE 2B 0C CE Команда flash_write (0x31). Далее уже мои догадки: 0x60 контрольная сумма, 0x0114 длина данных (4 адрес назначения + 272 полезная записываемая нагрузка), адрес назначения 0x0000F000, и уже дальше полезная нагрузка.

4F 4B OK

3A 00 00 00 Команда flash_write_check (0x3A).

4F 4B OK

60 00 00 00 Команда flash_xip_read_start (0x60).

4F 4B OK

3E 09 08 00 00 F0 00 00 10 01 00 00  Команда flash_xip_readSha (0x3E). 0x09 контрольная сумма, 0x0008 длина данных (8 байт). В данных наверное идут адрес начала 0x0000F000 и длина 0x00000110.

4F 4B 20 00 FD 6A F1 8F C4 AA F2 80 72 77 CA C7 67 CA 19 D1 2A F7 B5 5F 5E CB B8 90 2E F2 8B C2 43 05 24 AA Ответ OK, 0x0020 (32) байта, далее идут 32 байта хэша.

61 00 00 00 Команда flash_xip_read_finish (0x61).

4F 4B OK

30 E1 08 00 00 00 01 00 4F 88 01 00 Команда flash_erase (0x30). 0xE1 контрольная сумма, 0x0008 длина данных. В данных идут адрес начала 0x00010000 и адрес конца 0x0001884F. Т. е. стираются 0x8850 (34896) байт.

50 44 50 44 4F 4B PDPDOK

3F AD 04 08 00 00 01 80 FD 37 7A .. Команда flash_decompress_write (0x3F), в команде передаются упакованные данные для записи. 0xAD контрольная сумма, длина данных 0x0804 (2052 = 4 + 2048) байт. В данных идут адрес назначения 0x80010000 и далее уже 2048 байт данных.

4F 4B OK

...

Далее команды 0x3F повторяются, порциями по 2048 байт, пока данные не закончатся. В последнем блоке данных будет обычно меньше, чем 2048 байт:

3F ED A4 01 00 48 01 00 5E 35 0B .. Тут уже данных 0x01A4 (420) байт (4 байта адреса и 416 байт данных).

4F 4B OK

3A 00 00 00 Команда flash_write_check (0x3A).

4F 4B OK

60 00 00 00 Команда flash_xip_read_start (0x60).

4F 4B OK

3E E1 08 00 00 00 01 00 50 88 00 00 Команда flash_xip_readSha (0x3E). 0xE1 контрольная сумма, 0x0008 длина данных (8 байт). В данных наверное идут адрес начала 0x00010000 и длина 0x00008850 (34896) байт.

4F 4B 20 00 C7 FA 07 57 86 72 D5 C4 22 8B 61 3D B7 85 0D FC C3 C3 0D 8C A9 FF E4 76 48 EE 00 B9 6E 6F 32 6B Ответ OK, 0x0020 байт данных и контрольная сумма из 32 байт.

61 00 00 00 Команда flash_xip_read_finish (0x61). 

4F 4B OK

На этом процесс прошивки завершается.

Ниже представлен исходный код алгоритма вычисления 8-разрядной контрольной суммы (второй байт пакета). Этот код взят из репозитория исходного кода утилиты blisp [12].

blisp_return_t blisp_send_command(struct blisp_device* device,
                                  uint8_t command,
                                  void* payload,
                                  uint16_t payload_size,
                                  bool add_checksum) 
{ int ret; struct sp_port* serial_port = device->serial_port; device->tx_buffer[0] = command; device->tx_buffer[1] = 0; device->tx_buffer[2] = payload_size & 0xFF; device->tx_buffer[3] = (payload_size >> 8) & 0xFF; if (add_checksum)
{ uint32_t checksum = 0; checksum += device->tx_buffer[2] + device->tx_buffer[3]; for (uint16_t i = 0; i < payload_size; i++)
{ checksum += *(uint8_t*)((uint8_t*)payload + i); } device->tx_buffer[1] = checksum & 0xFF; } if (payload_size != 0)
{ memcpy(&device->tx_buffer[4], payload, payload_size); } ret = sp_blocking_write(serial_port, device->tx_buffer, 4 + payload_size, 1000); if (ret != (4 + payload_size))
{ blisp_dlog("Received error or not written all data: %d", ret); return BLISP_ERR_API_ERROR; } drain(serial_port); return BLISP_OK; }

Таким образом, контрольная сумма вычисляется очень просто: 

1. Подготавливается буфер размером (4 + payload_size) байт.

2. В байт *(буфер+0) записывается команда.

3. В *(буфер+2) записывается младший байт payload_size.

4. В *(буфер+3) записывается старший байт payload_size.

5. Тупо складываются байты payload_size (буфер[2], буфер[3]) и байты полезной нагрузки, результат накапливается в 32-битной переменной checksum.

6. В *(буфер+1) записывается младший байт 32-битной переменной checksum.

7. Через UART передаются все байты буфера, т. е. (4 + payload_size) байт.

[Ссылки]

1. BL602_ISP_protocol.pdf.
2. BL702's ISP protocol site:bouffalolab.com.
3. Pine64’s BL602 reverse engineering working group site:github.com.
4. Flashing Firmware to PineCone BL602 site:lupyuen.github.io.
5. BL602 EFlash Loader: Reverse Engineered with Ghidra site:lupyuen.github.io.
6. bflb-iot-tool, Python-утилита для прошивки микроконтроллеров Bouffalo.
7. Bouffalo Lab Dev Cube For Windows https://dev.bouffalolab.com/download.
8. Запуск Linux на Windows помощью WSL.
9. Сборка программ чипов Bouffalo в Linux.
10Сниферы USB.
11Bouffalo Flash Cube, инструкции по использованию.
12. pine64 / blisp site:github.com.
13blflash: прошивальщик BL602, написанный на Rust.

 

Добавить комментарий


Защитный код
Обновить

Top of Page