diff --git a/build.sh b/build.sh index fa02595..067a28a 100755 --- a/build.sh +++ b/build.sh @@ -6,13 +6,8 @@ PACKAGE_FILE_NAME="SC64" FILES=( "./fw/ft232h_config.xml" - "./fw/project/lcmxo2/impl1/sc64_impl1.bit" - "./fw/project/lcmxo2/impl1/sc64_impl1.jed" - "./sw/bootloader/build/bootloader.bin" - "./sw/controller/build/controller.bin" - "./sw/controller/build/controller.elf" - "./sw/pc/helpers.py" "./sw/pc/sc64.py" + "./sw/update/sc64.upd" "./LICENSE" "./README.md" ) @@ -75,6 +70,19 @@ build_update () { build_controller build_fpga + pushd sw/update > /dev/null + INFO="" + if [ ! -z "${GIT_BRANCH+x}" ]; then INFO+=" $GIT_BRANCH"; fi + if [ ! -z "${GIT_TAG+x}" ]; then INFO+=" $GIT_TAG"; fi + if [ ! -z "${GIT_SHA+x}" ]; then INFO+=" $GIT_SHA"; fi + python3 update.py \ + --info "$INFO" \ + --mcu ../controller/build/app/app.bin \ + --fpga ../../fw/project/lcmxo2/impl1/sc64_impl1.jed \ + --boot ../bootloader/build/bootloader.bin \ + sc64.upd + popd > /dev/null + BUILT_UPDATE=true } diff --git a/fw/project/lcmxo2/build.tcl b/fw/project/lcmxo2/build.tcl index 328fe4e..3dba79f 100644 --- a/fw/project/lcmxo2/build.tcl +++ b/fw/project/lcmxo2/build.tcl @@ -1,3 +1,2 @@ prj_project open sc64.ldf -prj_run Export -impl impl1 -task Bitgen prj_run Export -impl impl1 -task Jedecgen diff --git a/sw/controller/app.mk b/sw/controller/app.mk index 00eb1e9..d9534f3 100644 --- a/sw/controller/app.mk +++ b/sw/controller/app.mk @@ -19,6 +19,7 @@ SRC_FILES = \ task.c \ update.c \ usb.c +PAD_TO = 0x08008000 include common.mk diff --git a/sw/controller/common.mk b/sw/controller/common.mk index 54447b8..7dd1016 100644 --- a/sw/controller/common.mk +++ b/sw/controller/common.mk @@ -30,7 +30,7 @@ $(BUILD_DIR)/$(EXE_NAME).elf: $(OBJS) $(LD_SCRIPT) @$(OBJDUMP) -S -D $@ > $(BUILD_DIR)/$(EXE_NAME).lst $(BUILD_DIR)/$(EXE_NAME).bin: $(BUILD_DIR)/$(EXE_NAME).elf - $(OBJCOPY) -O binary $< $@ + $(OBJCOPY) -O binary --gap-fill 0xFF --pad-to $(PAD_TO) $< $@ $(BUILD_DIR)/$(EXE_NAME).hex: $(BUILD_DIR)/$(EXE_NAME).bin @$(OBJCOPY) -I binary -O ihex $< $@ diff --git a/sw/controller/loader.mk b/sw/controller/loader.mk index 97df37c..32917db 100644 --- a/sw/controller/loader.mk +++ b/sw/controller/loader.mk @@ -3,10 +3,12 @@ LD_SCRIPT = loader.ld BUILD_DIR = build/loader SRC_FILES = \ loader.S \ + flash.c \ fpga.c \ hw.c \ lcmxo2.c \ loader.c \ update.c +PAD_TO = 0x08001000 include common.mk diff --git a/sw/controller/src/flash.h b/sw/controller/src/flash.h index 8b7fa7e..1b65424 100644 --- a/sw/controller/src/flash.h +++ b/sw/controller/src/flash.h @@ -5,6 +5,9 @@ #include +#define FLASH_ERASE_BLOCK_SIZE (128 * 1024) + + void flash_erase_block (uint32_t offset); diff --git a/sw/controller/src/fpga.c b/sw/controller/src/fpga.c index 8c168d1..9edf7c6 100644 --- a/sw/controller/src/fpga.c +++ b/sw/controller/src/fpga.c @@ -40,9 +40,13 @@ void fpga_reg_set (fpga_reg_t reg, uint32_t value) { void fpga_mem_read (uint32_t address, size_t length, uint8_t *buffer) { fpga_cmd_t cmd = CMD_MEM_READ; uint8_t buffer_address = 0; + size_t dma_length = length; + if ((dma_length % 2) != 0) { + dma_length += 1; + } fpga_reg_set(REG_MEM_ADDRESS, address); - fpga_reg_set(REG_MEM_SCR, (length << MEM_SCR_LENGTH_BIT) | MEM_SCR_START); + fpga_reg_set(REG_MEM_SCR, (dma_length << MEM_SCR_LENGTH_BIT) | MEM_SCR_START); while (fpga_reg_get(REG_MEM_SCR) & MEM_SCR_BUSY); hw_spi_start(); @@ -55,6 +59,10 @@ void fpga_mem_read (uint32_t address, size_t length, uint8_t *buffer) { void fpga_mem_write (uint32_t address, size_t length, uint8_t *buffer) { fpga_cmd_t cmd = CMD_MEM_WRITE; uint8_t buffer_address = 0; + size_t dma_length = length; + if ((dma_length % 2) != 0) { + dma_length += 1; + } hw_spi_start(); hw_spi_trx((uint8_t *) (&cmd), 1, SPI_TX); @@ -63,17 +71,22 @@ void fpga_mem_write (uint32_t address, size_t length, uint8_t *buffer) { hw_spi_stop(); fpga_reg_set(REG_MEM_ADDRESS, address); - fpga_reg_set(REG_MEM_SCR, (length << MEM_SCR_LENGTH_BIT) | MEM_SCR_DIRECTION | MEM_SCR_START); + fpga_reg_set(REG_MEM_SCR, (dma_length << MEM_SCR_LENGTH_BIT) | MEM_SCR_DIRECTION | MEM_SCR_START); while (fpga_reg_get(REG_MEM_SCR) & MEM_SCR_BUSY); } void fpga_mem_copy (uint32_t src, uint32_t dst, size_t length) { + size_t dma_length = length; + if ((dma_length % 2) != 0) { + dma_length += 1; + } + fpga_reg_set(REG_MEM_ADDRESS, src); - fpga_reg_set(REG_MEM_SCR, (length << MEM_SCR_LENGTH_BIT) | MEM_SCR_START); + fpga_reg_set(REG_MEM_SCR, (dma_length << MEM_SCR_LENGTH_BIT) | MEM_SCR_START); while (fpga_reg_get(REG_MEM_SCR) & MEM_SCR_BUSY); fpga_reg_set(REG_MEM_ADDRESS, dst); - fpga_reg_set(REG_MEM_SCR, (length << MEM_SCR_LENGTH_BIT) | MEM_SCR_DIRECTION | MEM_SCR_START); + fpga_reg_set(REG_MEM_SCR, (dma_length << MEM_SCR_LENGTH_BIT) | MEM_SCR_DIRECTION | MEM_SCR_START); while (fpga_reg_get(REG_MEM_SCR) & MEM_SCR_BUSY); } diff --git a/sw/controller/src/hw.c b/sw/controller/src/hw.c index f5ccc29..74d3de3 100644 --- a/sw/controller/src/hw.c +++ b/sw/controller/src/hw.c @@ -313,10 +313,10 @@ void hw_loader_reset (loader_parameters_t *parameters) { RCC->APBENR1 |= RCC_APBENR1_PWREN | RCC_APBENR1_RTCAPBEN; PWR->CR1 |= PWR_CR1_DBP; TAMP->BKP0R = parameters->magic; - TAMP->BKP1R = parameters->mcu_address; - TAMP->BKP2R = parameters->mcu_length; + TAMP->BKP1R = parameters->flags; + TAMP->BKP2R = parameters->mcu_address; TAMP->BKP3R = parameters->fpga_address; - TAMP->BKP4R = parameters->fpga_length; + TAMP->BKP4R = parameters->bootloader_address; PWR->CR1 &= ~(PWR_CR1_DBP); RCC->APBENR1 &= ~(RCC_APBENR1_PWREN | RCC_APBENR1_RTCAPBEN); NVIC_SystemReset(); @@ -325,10 +325,10 @@ void hw_loader_reset (loader_parameters_t *parameters) { void hw_loader_get_parameters (loader_parameters_t *parameters) { RCC->APBENR1 |= RCC_APBENR1_PWREN | RCC_APBENR1_RTCAPBEN; parameters->magic = TAMP->BKP0R; - parameters->mcu_address = TAMP->BKP1R; - parameters->mcu_length = TAMP->BKP2R; + parameters->flags = TAMP->BKP1R; + parameters->mcu_address = TAMP->BKP2R; parameters->fpga_address = TAMP->BKP3R; - parameters->fpga_length = TAMP->BKP4R; + parameters->bootloader_address = TAMP->BKP4R; PWR->CR1 |= PWR_CR1_DBP; TAMP->BKP0R = 0; TAMP->BKP1R = 0; diff --git a/sw/controller/src/hw.h b/sw/controller/src/hw.h index edb7188..b8b073e 100644 --- a/sw/controller/src/hw.h +++ b/sw/controller/src/hw.h @@ -43,12 +43,18 @@ typedef enum { typedef uint64_t hw_flash_t; +typedef enum { + LOADER_FLAGS_UPDATE_MCU = (1 << 0), + LOADER_FLAGS_UPDATE_FPGA = (1 << 1), + LOADER_FLAGS_UPDATE_BOOTLOADER = (1 << 2), +} loader_parameters_flags_t; + typedef struct { uint32_t magic; + loader_parameters_flags_t flags; uint32_t mcu_address; - uint32_t mcu_length; uint32_t fpga_address; - uint32_t fpga_length; + uint32_t bootloader_address; } loader_parameters_t; diff --git a/sw/controller/src/update.c b/sw/controller/src/update.c index f03b77d..f36d160 100644 --- a/sw/controller/src/update.c +++ b/sw/controller/src/update.c @@ -1,3 +1,4 @@ +#include "flash.h" #include "fpga.h" #include "hw.h" #include "update.h" @@ -5,12 +6,15 @@ #include "vendor.h" -#define UPDATE_MAGIC_START (0x54535055) +#define UPDATE_MAGIC_START (0x54535055UL) +#define BOOTLOADER_ADDRESS (0x04E00000UL) +#define BOOTLOADER_LENGTH (0x001E0000UL) typedef enum { UPDATE_STATUS_MCU = 1, UPDATE_STATUS_FPGA = 2, + UPDATE_STATUS_BOOTLOADER = 3, UPDATE_STATUS_DONE = 0x80, UPDATE_STATUS_ERROR = 0xFF, } update_status_t; @@ -19,6 +23,7 @@ typedef enum { CHUNK_ID_UPDATE_INFO = 1, CHUNK_ID_MCU_DATA = 2, CHUNK_ID_FPGA_DATA = 3, + CHUNK_ID_BOOTLOADER_DATA = 4, } chunk_id_t; @@ -31,6 +36,13 @@ static uint8_t status_data[12] = { }; +static uint32_t update_align (uint32_t value) { + if ((value % 16) != 0) { + value += (16 - (value % 16)); + } + return value; +} + static uint32_t update_checksum (uint32_t address, uint32_t length) { uint8_t buffer[32]; uint32_t block_size; @@ -55,16 +67,21 @@ static uint32_t update_write_token (uint32_t *address) { static uint32_t update_prepare_chunk (uint32_t *address, chunk_id_t chunk_id) { uint32_t id = (uint32_t) (chunk_id); - uint32_t length = sizeof(id) + (2 * sizeof(uint32_t)); + uint32_t length = (4 * sizeof(uint32_t)); fpga_mem_write(*address, sizeof(id), (uint8_t *) (&id)); *address += length; return length; } static uint32_t update_finalize_chunk (uint32_t *address, uint32_t length) { + uint32_t chunk_length = ((4 * sizeof(uint32_t)) + length); + uint32_t aligned_chunk_length = update_align(chunk_length); + uint32_t aligned_length = aligned_chunk_length - (2 * sizeof(uint32_t)); uint32_t checksum = update_checksum(*address, length); - fpga_mem_write(*address - (2 * sizeof(uint32_t)), sizeof(length), (uint8_t *) (&length)); - fpga_mem_write(*address - sizeof(uint32_t), sizeof(checksum), (uint8_t *) (&checksum)); + fpga_mem_write(*address - (3 * sizeof(uint32_t)), sizeof(aligned_length), (uint8_t *) (&aligned_length)); + fpga_mem_write(*address - (2 * sizeof(uint32_t)), sizeof(checksum), (uint8_t *) (&checksum)); + fpga_mem_write(*address - sizeof(uint32_t), sizeof(length), (uint8_t *) (&length)); + length += (aligned_chunk_length - chunk_length); *address += length; return length; } @@ -83,16 +100,19 @@ static bool update_check_token (uint32_t *address) { static bool update_get_chunk (uint32_t *address, chunk_id_t *chunk_id, uint32_t *data_address, uint32_t *data_length) { uint32_t id; + uint32_t chunk_length; uint32_t checksum; fpga_mem_read(*address, sizeof(id), (uint8_t *) (&id)); *chunk_id = (chunk_id_t) (id); *address += sizeof(id); - fpga_mem_read(*address, sizeof(*data_length), (uint8_t *) (data_length)); - *address += sizeof(*data_length); + fpga_mem_read(*address, sizeof(chunk_length), (uint8_t *) (&chunk_length)); + *address += sizeof(chunk_length); fpga_mem_read(*address, sizeof(checksum), (uint8_t *) (&checksum)); *address += sizeof(checksum); + fpga_mem_read(*address, sizeof(*data_length), (uint8_t *) (data_length)); + *address += sizeof(*data_length); *data_address = *address; - *address += *data_length; + *address += (chunk_length - (2 * sizeof(uint32_t))); if (checksum != update_checksum(*data_address, *data_length)) { return true; } @@ -127,16 +147,16 @@ static void update_status_notify (update_status_t status) { update_error_t update_backup (uint32_t address, uint32_t *length) { - hw_flash_t buffer; uint32_t mcu_length; uint32_t fpga_length; + uint32_t bootloader_length; *length = update_write_token(&address); *length += update_prepare_chunk(&address, CHUNK_ID_MCU_DATA); mcu_length = hw_flash_size(); for (uint32_t offset = 0; offset < mcu_length; offset += sizeof(hw_flash_t)) { - buffer = hw_flash_read(offset); + hw_flash_t buffer = hw_flash_read(offset); fpga_mem_write(address + offset, sizeof(hw_flash_t), (uint8_t *) (&buffer)); } *length += update_finalize_chunk(&address, mcu_length); @@ -147,6 +167,13 @@ update_error_t update_backup (uint32_t address, uint32_t *length) { } *length += update_finalize_chunk(&address, fpga_length); + *length += update_prepare_chunk(&address, CHUNK_ID_BOOTLOADER_DATA); + bootloader_length = BOOTLOADER_LENGTH; + for (uint32_t offset = 0; offset < bootloader_length; offset += FPGA_MAX_MEM_TRANSFER) { + fpga_mem_copy(BOOTLOADER_ADDRESS + offset, address + offset, FPGA_MAX_MEM_TRANSFER); + } + *length += update_finalize_chunk(&address, bootloader_length); + return UPDATE_OK; } @@ -160,10 +187,10 @@ update_error_t update_prepare (uint32_t address, uint32_t length) { return UPDATE_ERROR_TOKEN; } + parameters.flags = 0; parameters.mcu_address = 0; - parameters.mcu_length = 0; parameters.fpga_address = 0; - parameters.fpga_length = 0; + parameters.bootloader_address = 0; while (address < end_address) { if (update_get_chunk(&address, &id, &data_address, &data_length)) { @@ -178,16 +205,24 @@ update_error_t update_prepare (uint32_t address, uint32_t length) { if (data_length > hw_flash_size()) { return UPDATE_ERROR_SIZE; } + parameters.flags |= LOADER_FLAGS_UPDATE_MCU; parameters.mcu_address = data_address; - parameters.mcu_length = data_length; break; case CHUNK_ID_FPGA_DATA: if (data_length > vendor_flash_size()) { return UPDATE_ERROR_SIZE; } + parameters.flags |= LOADER_FLAGS_UPDATE_FPGA; parameters.fpga_address = data_address; - parameters.fpga_length = data_length; + break; + + case CHUNK_ID_BOOTLOADER_DATA: + if (data_length > BOOTLOADER_LENGTH) { + return UPDATE_ERROR_SIZE; + } + parameters.flags |= LOADER_FLAGS_UPDATE_BOOTLOADER; + parameters.bootloader_address = data_address; break; default: @@ -209,12 +244,14 @@ bool update_check (void) { } void update_perform (void) { - hw_flash_t buffer; + uint32_t length; - if (parameters.mcu_length != 0) { + if (parameters.flags & LOADER_FLAGS_UPDATE_MCU) { + hw_flash_t buffer; update_status_notify(UPDATE_STATUS_MCU); + fpga_mem_read(parameters.mcu_address - 4, sizeof(length), (uint8_t *) (&length)); hw_flash_erase(); - for (uint32_t offset = 0; offset < parameters.mcu_length; offset += sizeof(hw_flash_t)) { + for (uint32_t offset = 0; offset < length; offset += sizeof(hw_flash_t)) { fpga_mem_read(parameters.mcu_address + offset, sizeof(hw_flash_t), (uint8_t *) (&buffer)); hw_flash_program(offset, buffer); if (hw_flash_read(offset) != buffer) { @@ -224,14 +261,26 @@ void update_perform (void) { } } - if (parameters.fpga_length != 0) { + if (parameters.flags & LOADER_FLAGS_UPDATE_FPGA) { update_status_notify(UPDATE_STATUS_FPGA); - if (vendor_update(parameters.fpga_address, parameters.fpga_length) != VENDOR_OK) { + fpga_mem_read(parameters.fpga_address - 4, sizeof(length), (uint8_t *) (&length)); + if (vendor_update(parameters.fpga_address, length) != VENDOR_OK) { update_status_notify(UPDATE_STATUS_ERROR); while (1); } } + if (parameters.flags & LOADER_FLAGS_UPDATE_BOOTLOADER) { + update_status_notify(UPDATE_STATUS_BOOTLOADER); + fpga_mem_read(parameters.bootloader_address - 4, sizeof(length), (uint8_t *) (&length)); + for (uint32_t offset = 0; offset < BOOTLOADER_LENGTH; offset += FLASH_ERASE_BLOCK_SIZE) { + flash_erase_block(BOOTLOADER_ADDRESS + offset); + } + for (uint32_t offset = 0; offset < length; offset += FPGA_MAX_MEM_TRANSFER) { + fpga_mem_copy(parameters.bootloader_address + offset, BOOTLOADER_ADDRESS + offset, FPGA_MAX_MEM_TRANSFER); + } + } + update_status_notify(UPDATE_STATUS_DONE); vendor_reconfigure(); diff --git a/sw/update/.gitignore b/sw/update/.gitignore new file mode 100644 index 0000000..aec73fe --- /dev/null +++ b/sw/update/.gitignore @@ -0,0 +1 @@ +*.upd diff --git a/sw/update/update.py b/sw/update/update.py new file mode 100644 index 0000000..ecae801 --- /dev/null +++ b/sw/update/update.py @@ -0,0 +1,216 @@ +import argparse +import math +import os +import sys +from binascii import crc32 +from datetime import datetime +from io import BufferedRandom + + +class JedecError(Exception): + pass + +class JedecFile: + __fuse_length: int = 0 + __fuse_offset: int = 0 + __fuse_data: bytes = b'' + __byte_buffer: int = 0 + + def __handle_q_field(self, f: BufferedRandom) -> None: + type = f.read(1) + if (type == b'F'): + value = b'' + while (True): + data = f.read(1) + if (data == b'*'): + value = value.decode('ascii', errors='backslashreplace') + if (not value.isdecimal()): + raise JedecError('Invalid Q field data') + self.__fuse_length = int(value) + break + else: + value += data + else: + self.__ignore_field(f) + + def __handle_l_field(self, f: BufferedRandom) -> None: + if (self.__fuse_length <= 0): + raise JedecError('Found fuse data before declaring fuse count') + + offset = b'' + while (True): + data = f.read(1) + if (data >= b'0' and data <= b'9'): + offset += data + elif (data == b'\r' or data == b'\n'): + offset = offset.decode('ascii', errors='backslashreplace') + if (not offset.isdecimal()): + raise JedecError('Invalid L field offset data') + offset = int(offset) + if (offset != self.__fuse_offset): + raise JedecError('Fuse data is not continuous') + break + else: + raise JedecError('Unexpected byte inside L field offset data') + + data = b'' + while (True): + data = f.read(1) + if (data == b'0' or data == b'1'): + shift = (7 - (self.__fuse_offset % 8)) + self.__byte_buffer |= (1 if data == b'1' else 0) << shift + if (((self.__fuse_offset % 8) == 7) or (self.__fuse_offset == (self.__fuse_length - 1))): + self.__fuse_data += int.to_bytes(self.__byte_buffer, 1, byteorder='little') + self.__byte_buffer = 0 + self.__fuse_offset += 1 + elif (data == b'\r' or data == b'\n'): + pass + elif (data == b'*'): + break + elif (data == b''): + raise JedecError('Unexpected end of file') + else: + raise JedecError('Unexpected byte inside L field fuse data') + + def __ignore_field(self, f: BufferedRandom) -> None: + data = None + while (data != b'*'): + data = f.read(1) + if (data == b''): + raise JedecError('Unexpected end of file') + + def parse(self, path: str) -> bytes: + self.__fuse_length = 0 + self.__fuse_offset = 0 + self.__fuse_data = b'' + self.__byte_buffer = 0 + + field = None + with open(path, 'rb+') as f: + while (True): + field = f.read(1) + if (field == b'\x02'): + f.seek(-1, os.SEEK_CUR) + break + elif (field == b''): + raise JedecError('Unexpected end of file') + + while (True): + field = f.read(1) + if (field == b'Q'): + self.__handle_q_field(f) + elif (field == b'L'): + self.__handle_l_field(f) + elif (field == b'\r' or field == b'\n'): + pass + elif (field == b'\x03'): + break + elif (field == b''): + raise JedecError('Unexpected end of file') + else: + self.__ignore_field(f) + + if (self.__fuse_length <= 0): + raise JedecError('No fuse data found') + + if (self.__fuse_offset != self.__fuse_length): + raise JedecError('Missing fuse data inside JEDEC file') + + if (len(self.__fuse_data) != math.ceil(self.__fuse_length / 8)): + raise JedecError('Missing fuse data inside JEDEC file') + + return self.__fuse_data + + +class SC64UpdateData: + __UPDATE_TOKEN = b'SC64 Update v2.0' + + __CHUNK_ID_UPDATE_INFO = 1 + __CHUNK_ID_MCU_DATA = 2 + __CHUNK_ID_FPGA_DATA = 3 + __CHUNK_ID_BOOTLOADER_DATA = 4 + + __data = b'' + + def __int_to_bytes(self, value: int) -> bytes: + return value.to_bytes(4, byteorder='little') + + def __align(self, value: int) -> int: + if (value % 16 != 0): + value += (16 - (value % 16)) + return value + + def __add_chunk(self, id: int, data: bytes) -> None: + chunk = b'' + chunk_length = (16 + len(data)) + aligned_length = self.__align(chunk_length) + chunk += self.__int_to_bytes(id) + chunk += self.__int_to_bytes(aligned_length - 8) + chunk += self.__int_to_bytes(crc32(data)) + chunk += self.__int_to_bytes(len(data)) + chunk += data + chunk += bytes([0] * (aligned_length - chunk_length)) + self.__data += chunk + + def create_update_data(self) -> None: + self.__data = self.__UPDATE_TOKEN + + def add_update_info(self, data: bytes) -> None: + self.__add_chunk(self.__CHUNK_ID_UPDATE_INFO, data) + + def add_mcu_data(self, data: bytes) -> None: + self.__add_chunk(self.__CHUNK_ID_MCU_DATA, data) + + def add_fpga_data(self, data: bytes) -> None: + self.__add_chunk(self.__CHUNK_ID_FPGA_DATA, data) + + def add_bootloader_data(self, data: bytes) -> None: + self.__add_chunk(self.__CHUNK_ID_BOOTLOADER_DATA, data) + + def get_update_data(self) -> bytes: + return self.__data + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='SC64 update file generator') + parser.add_argument('--info', metavar='info_text', required=False, help='text to embed as update info') + parser.add_argument('--mcu', metavar='mcu_path', required=False, help='path to MCU update data') + parser.add_argument('--fpga', metavar='fpga_path', required=False, help='path to FPGA update data') + parser.add_argument('--boot', metavar='bootloader_path', required=False, help='path to N64 bootloader update data') + parser.add_argument('output', metavar='output_path', help='path to final update data') + + if (len(sys.argv) <= 1): + parser.print_help() + parser.exit() + + args = parser.parse_args() + + try: + update = SC64UpdateData() + update.create_update_data() + + creation_time = datetime.now().strftime('%d.%m.%Y %H:%M:%S') + info_text = args.info or '' + update_info = f'{creation_time}{info_text}' + print(f'Update info text: {update_info}') + update.add_update_info(update_info.encode()) + + if (args.mcu): + with open(args.mcu, 'rb+') as f: + update.add_mcu_data(f.read()) + + if (args.fpga): + update.add_fpga_data(JedecFile().parse(args.fpga)) + + if (args.boot): + with open(args.boot, 'rb+') as f: + update.add_bootloader_data(f.read()) + + with open(args.output, 'wb+') as f: + f.write(update.get_update_data()) + except JedecError as e: + print(f'Error while parsing FPGA update data: {e}') + exit(-1) + except IOError as e: + print(f'IOError: {e}') + exit(-1)