Микроконтроллеры Bouffalo серии BL602 поддерживают перепрошивку через UART/SDIO. Секция исполняемой программы через интерфейс UART/SDIO может быть загружена в RAM для запуска. Структура загружаемой программы должна удовлетворять формату загрузчика (BL602 Bootrom). Для приложений, которые не активируют настройки безопасности (т. е. приложения, где не разрешены encryption и signatures), загружаемый образ показан на следующем рисунке.
Рис. 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:
Рис. 2. UART/SDIO boot image, с шифрованием и цифровой подписью (encrypted & signed).
В сравнении с обычными загружаемыми образами, в образах "encrypted & signed" область BootInfo должна включать такую информацию, как публичные ключи (public keys), цифровые подписи (signatures), и AES IV.
Формат файла загружаемого образа, где разрешены настройки encryption, но без подписи:
Рис. 3. UART/SDIO boot image, с шифрованием, но без цифровой подписи (encrypted & no signature).
Формат файла загружаемого образа, где разрешена подпись, но не применено шифрование:
Рис. 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 выглядит следующим образом:
Код 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).
Если генерируется только загружаемый образ 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:
Рис. 7. Главный экран Linux-версии BLFlashCube (tools/bflb_tools/bouffalo_flash_cube/BLFlashCube-ubuntu).
Замечание: для запуска BLFlashCube-ubuntu могут понадобиться права sudo.
BLFlashCommand-ubuntu. Утилита командной строки для прошивки микроконтроллеров Bouffalo. Пример запуска для прошивки чипа BL602:
В этом примере команда запускается из корневого каталога проекта, в котором находится файл конфигурации flash_prog_cfg.ini (например, проект из SDK bouf_hello_world). Назначение опций очевидно и не требует специального описания. Пример содержимого файла flash_prog_cfg.ini:
bflb-iot-tool. Это еще одна утилита командной строки, написанная на Python [6]. По исходному коду можно самому разобраться в протоколе прошивки, и прошивать чипы Biuffalo не только утилитами PC, но и другим микроконтроллером. Пример запуска:
В этом примере утилита 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 байт:
Хост посылает эту команду только когда образ подписан. Если подписи нет, то отправка этой команды должна быть пропущена. 68-байтная структура публичного ключа следующая:
__PACKED_STRUCT pkey_cfg_t
{
uint8_t eckeyx[32]; //ec key in boot infouint8_t eckeyy[32]; //ec key in boot infouint32_t crc32;
};
Хост посылает эту команду только когда образ подписан. Если подписи нет, то отправка этой команды должна быть пропущена. 68-байтная структура публичного ключа следующая:
Эффективная длина подписи (signature) не фиксирована, и её структура следующая:
Хост посылает эту команду только когда образ зашифрован. Если шифрования нет, то отправка этой команды должна быть пропущена. 68-байтная структура публичного ключа следующая:
Образ загрузки UART/SDIO (boot image) поддерживает несколько сегментов, и данные и код каждого сегмента может быть загружен программой загрузчика по адресу, указанному заголовком сегмента (Segment Header). Количество сегментов в образе определяется полем segment_cnt структуры bootheader_t (см. выше "Load boot header"). Хост должен записать эту переменную во время обработки boot header, и затем организовать цикл загрузки заголовка сегмента (Load Segment Header) и данных сегмента (Load Segment Data) в количестве segment_cnt итераций.
Для данных сегмента из-за того, что есть ограничение в 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):
Для программы, которая не требует шифрования и цифровой подписи, и когда в ней только один сегмент, процесс загрузки можно описать следующим образом (на примере чипа 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:
Рис. 8. Образ Eflash_Loader.
С помощью выполнения шагов 1 .. 13, описанных выше в разделе "Алгоритм процесса загрузки" вы можете загрузить и запустить образ Eflash_loader.
Образы RAM-загрузчика. Двоичные файлы образов Eflash_Loader (*.bin) можно найти в Bouffalo SDK, в каталоге утилиты прошивальщика. Например, для чипа BL602 это может быть папка наподобие tools/bflb_tools/bouffalo_flash_cube/chips/bl602/eflash_loader/:
Обратите внимание, что в именах файлов закодирована тактовая частота, с которой работает чип. Т. е. 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 следующими командами протокола.
Эта команда выполняет стирание всей памяти flash. Данные, участвующие в вычислении контрольной суммы, это все данные после байтов cksum (следующие инструкции одинаковы). Проверка необязательна. Если вы не хотите включать проверку, вы можете установить cksum равным 0.
Предположим, что длина данных, участвующих в вычислении контрольной суммы, равна data_len (включая младший и старший байты длины Len_lsb и Len_msb). Тогда псевдокод для вычисления контрольной суммы будет следующий:
Запишет Nbytes байт данных по указанному адресу Flash. Из-за ограничения на размер буфера, используемого в Eflash_loader, максимальная полезная нагрузка этой команды ограничена 8 килобайтами.
Эта команда используется для проверки, была ли ошибка в процессе программирования данных flash, после того как все программируемые данные были переданы. Если программирование Flash было корректным, то будет возвращен ответ OK. Иначе будет ответ FL+ error code, где error code равен BFLB_EFLASH_LOADER_FLASH_WRITE_ERROR, подробнее см. описание фрейма ответа ошибки (error response frame).
Эта команда вычитывает Nbytes байт данных по указанному адресу Flash. Из-за ограничения на размер буфера Eflash_loader максимальный размер блока считываемых данных Read_len составляет 8K.
Эта команда используется для быстрой проверки корректности программирования Flash. Хост посылает начальный адрес данных flash, по которым вычисляется хэш, и BL602 вернет соответствующее значение SHA256. Хост также синхронно вычисляет SHA256 от только что прошитого файла, и затем сравнивает свою SHA256 и SHA256 возвращенного результата, чтобы определить, что память Flash была корректно запрограммирована.
Описанные выше фреймы обмена BL602 и хоста соответствуют ситуациям, когда ошибки отсутствуют. В случае ошибки Eflash_loader возвратит следующий фрейм ошибки:
Плохая новость - описание протокола 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). Назначение почти всех команд можно угадать по их имени:
Этот сеанс обмена данными был снят с помощью утилиты Device Monitoring Studio [10] на ноутбуке под Windows 10. Прошивальщик bflb-iot-tool запускался на Ubintu 22.04 под виртуальной машиной WSL, работающей на том же ноутбуке, благодаря чему Device Monitoring Studio получила возможность обработать данные протокола.
Обмен происходит строго по сценарию запрос от 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) и какая-то другая информация.
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.
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) байта.
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 байт.
Ниже представлен исходный код алгоритма вычисления 8-разрядной контрольной суммы (второй байт пакета). Этот код взят из репозитория исходного кода утилиты blisp [12].
blisp_return_tblisp_send_command(struct blisp_device* device,
uint8_t command,
void* payload,
uint16_t payload_size,
bool add_checksum) {
int ret;
structsp_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) байт.