add a method to convert binary content to UF2

will be used in concatenate in order to write .uf2 files

Signed-off-by: Brian S. Stephan <bss@incorporeal.org>
This commit is contained in:
Brian S. Stephan 2024-01-06 20:49:39 -06:00
parent 314fc909ff
commit d6857d5da1
Signed by: bss
GPG Key ID: 3DE06D3180895FCB
3 changed files with 65 additions and 4 deletions

View File

@ -6,6 +6,7 @@ SPDX-License-Identifier: MIT
import argparse import argparse
import copy import copy
import logging import logging
import struct
from typing import Optional from typing import Optional
from google.protobuf.message import Message from google.protobuf.message import Message
@ -21,6 +22,11 @@ logger = logging.getLogger(__name__)
GP2040CE_START_ADDRESS = 0x10000000 GP2040CE_START_ADDRESS = 0x10000000
GP2040CE_SIZE = 2 * 1024 * 1024 GP2040CE_SIZE = 2 * 1024 * 1024
UF2_FAMILY_ID = 0xE48BFF56
UF2_MAGIC_FIRST = 0x0A324655
UF2_MAGIC_SECOND = 0x9E5D5157
UF2_MAGIC_FINAL = 0x0AB16F30
################# #################
# LIBRARY ITEMS # # LIBRARY ITEMS #
@ -106,6 +112,38 @@ def concatenate_firmware_and_storage_files(firmware_filename: str,
write(endpoint_out, endpoint_in, GP2040CE_START_ADDRESS, bytes(new_binary)) write(endpoint_out, endpoint_in, GP2040CE_START_ADDRESS, bytes(new_binary))
def convert_binary_to_uf2(binary: bytearray) -> bytearray:
"""Convert a GP2040-CE binary payload to Microsoft's UF2 format.
https://github.com/microsoft/uf2/tree/master#overview
Args:
binary: bytearray content to convert to a UF2 payload
Returns:
the content in UF2 format
"""
size = len(binary)
blocks = (len(binary) // 256) + 1 if len(binary) % 256 else len(binary) // 256
uf2 = bytearray()
index = 0
while index < size:
pad_count = 476 - len(binary[index:index+256])
uf2 += struct.pack('<LLLLLLLL',
UF2_MAGIC_FIRST, # first magic number
UF2_MAGIC_SECOND, # second magic number
0x00002000, # familyID present
0x10000000 + index, # address to write to
256, # bytes to write in this block
index // 256, # sequential block number
blocks, # total number of blocks
UF2_FAMILY_ID) # family ID
uf2 += binary[index:index+256] + bytearray(b'\x00' * pad_count) # content
uf2 += struct.pack('<L', UF2_MAGIC_FINAL) # final magic number
index += 256
return uf2
def get_gp2040ce_from_usb() -> tuple[bytes, object, object]: def get_gp2040ce_from_usb() -> tuple[bytes, object, object]:
"""Read the firmware + config sections from a USB device. """Read the firmware + config sections from a USB device.

View File

@ -52,9 +52,22 @@ def storage_dump():
@pytest.fixture @pytest.fixture
def whole_board_dump(): def whole_board_dump():
"""Read in a test whole board dump file of a GP2040-CE board.""" """Read in a test whole board dump file of a GP2040-CE board.
NOTE: this is from a 16 MB flash because I used an ABB for this test.
"""
filename = os.path.join(HERE, 'test-files', 'test-whole-board.bin') filename = os.path.join(HERE, 'test-files', 'test-whole-board.bin')
with open(filename, 'rb') as file: with open(filename, 'rb') as file:
content = file.read() content = file.read()
yield content yield content
@pytest.fixture
def whole_board_with_board_config_dump():
"""Read in a test whole board dump file of a GP2040-CE board plus board config."""
filename = os.path.join(HERE, 'test-files', 'test-whole-board-with-board-config.bin')
with open(filename, 'rb') as file:
content = file.read()
yield content

View File

@ -12,9 +12,10 @@ from decorator import decorator
from gp2040ce_bintools import get_config_pb2 from gp2040ce_bintools import get_config_pb2
from gp2040ce_bintools.builder import (FirmwareLengthError, combine_firmware_and_config, from gp2040ce_bintools.builder import (FirmwareLengthError, combine_firmware_and_config,
concatenate_firmware_and_storage_files, get_gp2040ce_from_usb, concatenate_firmware_and_storage_files, convert_binary_to_uf2,
pad_binary_up_to_board_config, pad_binary_up_to_user_config, get_gp2040ce_from_usb, pad_binary_up_to_board_config,
replace_config_in_binary, write_new_config_to_filename, write_new_config_to_usb) pad_binary_up_to_user_config, replace_config_in_binary,
write_new_config_to_filename, write_new_config_to_usb)
from gp2040ce_bintools.storage import (get_board_storage_section, get_config, get_config_footer, from gp2040ce_bintools.storage import (get_board_storage_section, get_config, get_config_footer,
get_user_storage_section, serialize_config_with_footer) get_user_storage_section, serialize_config_with_footer)
@ -75,6 +76,15 @@ def test_concatenate_both_configs_to_file(tmp_path):
assert footer_size == 3309 assert footer_size == 3309
def test_convert_binary_to_uf2(whole_board_with_board_config_dump):
"""Do some sanity checks in the attempt to convert a binary to a UF2."""
uf2 = convert_binary_to_uf2(whole_board_with_board_config_dump)
assert len(uf2) == 4194304 # binary is 8192 256 byte chunks, UF2 is 512 b per chunk
assert uf2[0:4] == b'\x55\x46\x32\x0a' == b'UF2\n' # proper magic
assert uf2[8:12] == bytearray(b'\x00\x20\x00\x00') # family ID set
assert uf2[524:528] == bytearray(b'\x00\x01\x00\x10') # address to write the second chunk
@with_pb2s @with_pb2s
def test_concatenate_user_json_to_file(tmp_path): def test_concatenate_user_json_to_file(tmp_path):
"""Test that we write a file with firmware + JSON user config as expected.""" """Test that we write a file with firmware + JSON user config as expected."""