From da9c3f6aede557fcae803a78c7aae309d3e19d28 Mon Sep 17 00:00:00 2001 From: Polprzewodnikowy Date: Fri, 19 Nov 2021 00:18:46 +0100 Subject: [PATCH] dum.py rewrite --- docker_build.sh | 1 + sw/pc/dum.py | 294 +++++++++++++++++++++++++++++++++------------ sw/pc/update.py | 97 --------------- sw/riscv/src/cfg.c | 4 + sw/riscv/src/cfg.h | 1 + sw/riscv/src/usb.c | 6 + 6 files changed, 228 insertions(+), 175 deletions(-) delete mode 100644 sw/pc/update.py diff --git a/docker_build.sh b/docker_build.sh index b86f26a..f80869e 100755 --- a/docker_build.sh +++ b/docker_build.sh @@ -1,6 +1,7 @@ #!/bin/bash docker run \ + --rm \ --user $(id -u):$(id -g) \ --mount type=bind,src="$(pwd)",target="/workdir" \ ghcr.io/polprzewodnikowy/sc64env:v1.2 \ diff --git a/sw/pc/dum.py b/sw/pc/dum.py index e4374c2..2f06f17 100644 --- a/sw/pc/dum.py +++ b/sw/pc/dum.py @@ -1,34 +1,149 @@ +from serial import Serial, SerialException +from serial.tools import list_ports +import argparse import os -import serial import sys +import time + + + +class SC64Exception(Exception): + pass class SC64: - __SDRAM_SIZE = 64 * 1024 * 1024 + __CFG_ID_SCR = 0 + __CFG_ID_SDRAM_SWITCH = 1 + __CFG_ID_SDRAM_WRITABLE = 2 + __CFG_ID_DD_ENABLE = 3 + __CFG_ID_SAVE_TYPE = 4 + __CFG_ID_CIC_SEED = 5 + __CFG_ID_TV_TYPE = 6 + __CFG_ID_SAVE_OFFEST = 7 + __CFG_ID_DD_OFFEST = 8 + __CFG_ID_SKIP_BOOTLOADER = 9 + __CFG_ID_FLASH_OPERATION = 10 + __CFG_ID_RECONFIGURE = 11 - __CONFIG_QUERY_SAVE_TYPE = 4 - __CONFIG_QUERY_SAVE_OFFSET = 7 + __SC64_VERSION_V2 = 0x53437632 - def __init__(self, port): - self.__serial = serial.Serial(port) - self.__save_type = self.__query_config(self.__CONFIG_QUERY_SAVE_TYPE) - self.__save_offset = self.__query_config(self.__CONFIG_QUERY_SAVE_OFFSET) - print('{:08X}'.format(self.__save_type)) - print('{:08X}'.format(self.__save_offset)) + __UPDATE_OFFSET = 0x03B60000 + + __MIN_ROM_LENGTH = 0x101000 + __DDIPL_ROM_LENGTH = 0x400000 + + __serial = None - def __query_config(self, query): - self.__serial.write(b'CMDQ') - self.__serial.write(query.to_bytes(4, byteorder='big')) - self.__serial.write(bytes(4)) - value = self.__serial.read(4) - if (self.__serial.read(4).decode() != 'CMPQ'): - raise Exception('Bad query response') - return int.from_bytes(value, byteorder='big') + def __init__(self) -> None: + self.__find_sc64() - def __save_length(self): + def __del__(self) -> None: + if (self.__serial): + self.__serial.close() + + + def __align(self, value: int, align: int) -> int: + return (value + (align - 1)) & ~(align - 1) + + + def __read(self, bytes: int) -> bytes: + return self.__serial.read(bytes) + + + def __write(self, data: bytes) -> None: + self.__serial.write(data) + + + def __write_dummy(self, length: int) -> None: + self.__write(bytes(length)) + + + def __read_int(self) -> int: + return int.from_bytes(self.__read(4), byteorder="big") + + + def __write_int(self, value: int) -> None: + self.__write(value.to_bytes(4, byteorder="big")) + + + def __read_cmd_status(self, cmd: str) -> None: + if (self.__read(4).decode() != f"CMP{cmd[0]}"): + raise SC64Exception("Wrong command response") + + + def __write_cmd(self, cmd: str, arg1: int, arg2: int) -> None: + self.__write(f"CMD{cmd[0]}".encode()) + self.__write_int(arg1) + self.__write_int(arg2) + + + def __find_sc64(self) -> None: + ports = list_ports.comports() + device_found = False + + for p in ports: + if (p.vid == 0x0403 and p.pid == 0x6014 and p.serial_number.startswith("SC64")): + try: + self.__serial = Serial(p.device, timeout=10.0, write_timeout=10.0) + self.__probe_device() + except SerialException or SC64Exception: + if (self.__serial): + self.__serial.close() + continue + device_found = True + break + + if (not device_found): + raise SC64Exception("No SummerCart64 device was found") + + + def __probe_device(self) -> None: + self.__write_cmd("V", 0, 0) + version = self.__read_int() + self.__read_cmd_status("V") + if (version != self.__SC64_VERSION_V2): + raise SC64Exception(f"Unknown hardware version: {hex(version)}") + + + def __query_config(self, id: int, arg: int = 0) -> int: + self.__write_cmd("Q", id, arg) + value = self.__read_int() + self.__read_cmd_status("Q") + return value + + + def __change_config(self, id: int, arg: int = 0, ignore_response: bool = False) -> None: + self.__write_cmd("C", id, arg) + if (not ignore_response): + self.__read_cmd_status("C") + + + def __read_file_from_sdram(self, file: str, offset: int, length: int, align: int = 2) -> None: + with open(file, "wb") as f: + transfer_size = self.__align(length, align) + self.__write_cmd("R", offset, transfer_size) + f.write(self.__read(length)) + if (transfer_size != length): + self.__read(transfer_size - length) + self.__read_cmd_status("R") + + + def __write_file_to_sdram(self, file: str, offset: int, align: int = 2, min_length: int = 0) -> None: + with open(file, "rb") as f: + length = os.fstat(f.fileno()).st_size + transfer_size = self.__align(max(length, min_length), align) + self.__write_cmd("W", offset, transfer_size) + self.__write(f.read()) + if (transfer_size != length): + self.__write_dummy(transfer_size - length) + self.__read_cmd_status("W") + + + def __get_save_length(self) -> None: + save_type = self.__query_config(self.__CFG_ID_SAVE_TYPE) return { 0: 0, 1: 512, @@ -38,71 +153,94 @@ class SC64: 5: (3 * 32 * 1024), 6: (128 * 1024), 7: 0 - }[self.__save_type] + }[save_type] - def dump_save(self, file): - length = self.__save_length() - if (length == 0): - raise Exception('No save type is selected') - self.__serial.write(b'CMDR') - self.__serial.write(self.__save_offset.to_bytes(4, byteorder='big')) - self.__serial.write(length.to_bytes(4, byteorder='big')) - save = self.__serial.read(length) - response = self.__serial.read(4) - if (response.decode() == 'CMPR'): - with open(file, 'wb') as f: - f.write(save) + def __reconfigure(self) -> None: + magic = self.__query_config(self.__CFG_ID_RECONFIGURE) + self.__change_config(self.__CFG_ID_RECONFIGURE, magic, ignore_response=True) + time.sleep(0.2) + + + def update_firmware(self, file: str, backup_file: str = None) -> None: + if (backup_file != None): + backup_length = self.__query_config(self.__CFG_ID_FLASH_OPERATION, self.__UPDATE_OFFSET) + self.__read_file_from_sdram(backup_file, self.__UPDATE_OFFSET, backup_length) + self.__write_file_to_sdram(file, self.__UPDATE_OFFSET) + self.__change_config(self.__CFG_ID_FLASH_OPERATION, self.__UPDATE_OFFSET) + self.__reconfigure() + self.__probe_device() + + + def set_skip_bootloader(self, enable: bool) -> None: + self.__change_config(self.__CFG_ID_SKIP_BOOTLOADER, 1 if enable else 0) + + + def download_rom(self, file: str, length: int, offset: int = 0) -> None: + self.__read_file_from_sdram(file, offset, length) + + + def upload_rom(self, file: str, offset: int = 0) -> None: + self.__write_file_to_sdram(file, offset, min_length=self.__MIN_ROM_LENGTH) + + + def set_save_type(self, type: int) -> None: + if (type >= 0 and type <= 6): + self.__change_config(self.__CFG_ID_SAVE_TYPE, type) else: - raise Exception('There was a problem while dumping save data') + raise SC64Exception("Save type outside of supported values") - def redump_save(self, file): - length = self.__save_length() - if (not length): - raise Exception('No save type is selected') - if (os.path.getsize(file) != length): - raise Exception('Wrong save file size') - with open(file, 'rb') as f: - self.__serial.write(b'CMDW') - self.__serial.write(self.__save_offset.to_bytes(4, byteorder='big')) - self.__serial.write(length.to_bytes(4, byteorder='big')) - self.__serial.write(f.read()) - response = self.__serial.read(4) - if (response.decode() != 'CMPW'): - raise Exception('There was a problem while redumping save data') - - - def dump_all(self, file): - self.__serial.write(b'CMDR') - self.__serial.write((0).to_bytes(4, byteorder='big')) - self.__serial.write((self.__SDRAM_SIZE).to_bytes(4, byteorder='big')) - save = self.__serial.read(self.__SDRAM_SIZE) - response = self.__serial.read(4) - if (response.decode() == 'CMPR'): - with open(file, 'wb') as f: - f.write(save) + def download_save(self, file: str) -> None: + length = self.__get_save_length() + if (length > 0): + offset = self.__query_config(self.__CFG_ID_SAVE_OFFEST) + self.__read_file_from_sdram(file, offset, length) else: - raise Exception('There was a problem while dumping all data') + raise SC64Exception("Can't read save data - no save type is set") + + + def upload_save(self, file: str) -> None: + length = self.__get_save_length() + save_length = os.path.getsize(file) + if (length <= 0): + raise SC64Exception("Can't write save data - no save type is set") + elif (length != save_length): + raise SC64Exception("Can't write save data - save file size is different than expected") + else: + offset = self.__query_config(self.__CFG_ID_SAVE_OFFEST) + self.__write_file_to_sdram(file, offset) + + + def set_dd_enable(self, enable: bool) -> None: + self.__change_config(self.__CFG_ID_DD_ENABLE, 1 if enable else 0) + + + def download_dd_ipl(self, file: str) -> None: + dd_ipl_offset = self.__query_config(self.__CFG_ID_DD_OFFEST) + self.__read_file_from_sdram(file, dd_ipl_offset, length=self.__DDIPL_ROM_LENGTH) + + + def upload_dd_ipl(self, file: str, offset: int = None) -> None: + if (offset != None): + self.__change_config(self.__CFG_ID_DD_OFFEST, offset) + dd_ipl_offset = self.__query_config(self.__CFG_ID_DD_OFFEST) + self.__write_file_to_sdram(file, dd_ipl_offset, min_length=self.__DDIPL_ROM_LENGTH) -mode = 'r' -file = 'save.dat' -port = 'COM7' - -if (len(sys.argv) >= 2): - mode = sys.argv[1] -if (len(sys.argv) >= 3): - file = sys.argv[2] -if (len(sys.argv) >= 4): - port = sys.argv[3] - -sc64 = SC64(port) - -if (mode == 'r'): - sc64.dump_save(file) -elif (mode == 'w'): - sc64.redump_save(file) -elif (mode == 'a'): - sc64.dump_all(file) +if __name__ == "__main__": + try: + sc64 = SC64() + sc64.update_firmware("../../fw/output_files/SC64_update.bin", "SC64_backup.bin") + sc64.set_skip_bootloader(False) + sc64.set_save_type(1) + sc64.set_dd_enable(True) + sc64.upload_rom("rom.z64") + sc64.upload_dd_ipl("ddipl.z64") + sc64.upload_save("saves/save.eep") + sc64.download_rom("rom.bin", 0x1000) + sc64.download_save("save.bin") + sc64.download_dd_ipl("dd_ipl.bin") + except SC64Exception as e: + print(e) diff --git a/sw/pc/update.py b/sw/pc/update.py deleted file mode 100644 index 43978f3..0000000 --- a/sw/pc/update.py +++ /dev/null @@ -1,97 +0,0 @@ -import os -import serial -import time -import filecmp - - - -class SC64: - __CFG_ID_FLASH_OPERATION = 10 - __CFG_ID_RECONFIGURE = 11 - - - def __init__(self, port): - self.__serial = serial.Serial(port, timeout=10.0, write_timeout=10.0) - - - def __query_config(self, query, arg=0): - self.__serial.write(b'CMDQ') - self.__serial.write(query.to_bytes(4, byteorder='big')) - self.__serial.write(arg.to_bytes(4, byteorder='big')) - value = self.__serial.read(4) - if (self.__serial.read(4).decode() != 'CMPQ'): - raise Exception('Bad query response') - return int.from_bytes(value, byteorder='big') - - - def __change_config(self, change, arg=0, ignore_response=False): - self.__serial.write(b'CMDC') - self.__serial.write(change.to_bytes(4, byteorder='big')) - self.__serial.write(arg.to_bytes(4, byteorder='big')) - if (not ignore_response and self.__serial.read(4).decode() != 'CMPC'): - raise Exception('Bad change response') - - - def reconfigure(self): - magic = self.__query_config(self.__CFG_ID_RECONFIGURE) - self.__change_config(self.__CFG_ID_RECONFIGURE, magic, ignore_response=True) - time.sleep(0.2) - - - def read_flash(self, file): - size = self.__query_config(self.__CFG_ID_FLASH_OPERATION) - print(f'Flash size: {(size / 1024.0):1.1f} kB') - self.__serial.write(b'CMDR') - self.__serial.write((0).to_bytes(4, byteorder='big')) - self.__serial.write((size).to_bytes(4, byteorder='big')) - flash = self.__serial.read(size) - response = self.__serial.read(4) - if (response.decode() == 'CMPR'): - with open(file, 'wb') as f: - f.write(flash) - else: - raise Exception('There was a problem while reading flash data') - - - def program_flash(self, file): - length = os.path.getsize(file) - offset = 0 - with open(file, 'rb') as f: - self.__serial.write(b'CMDW') - self.__serial.write(offset.to_bytes(4, byteorder='big')) - self.__serial.write(length.to_bytes(4, byteorder='big')) - self.__serial.write(f.read()) - response = self.__serial.read(4) - if (response.decode() != 'CMPW'): - raise Exception('There was a problem while sending flash data') - self.__change_config(self.__CFG_ID_FLASH_OPERATION) - - - -file = '../../fw/output_files/SC64_update.bin' -backup_file = 'SC64_backup.bin' -verify_file = 'SC64_update_verify.bin' -port = 'COM7' - -sc64 = SC64(port) - -print('Making backup...') -sc64.read_flash(backup_file) -print('done\n') - -print('Flashing... ') -sc64.program_flash(file) -print('done\n') - -print('Reconfiguring... ') -sc64.reconfigure() -print('done\n') - -print('Verifying... ') -sc64.read_flash(verify_file) -if (filecmp.cmp(file, verify_file)): - print('success!\n') -else: - print('failure.\n') - -print('Update done!') diff --git a/sw/riscv/src/cfg.c b/sw/riscv/src/cfg.c index ff9923d..8b18ea0 100644 --- a/sw/riscv/src/cfg.c +++ b/sw/riscv/src/cfg.c @@ -103,6 +103,10 @@ static void set_save_type (enum save_type save_type) { } +uint32_t cfg_get_version (void) { + return CFG->VERSION; +} + void cfg_update (uint32_t *args) { switch (args[0]) { case CFG_ID_SCR: diff --git a/sw/riscv/src/cfg.h b/sw/riscv/src/cfg.h index 9377005..1312835 100644 --- a/sw/riscv/src/cfg.h +++ b/sw/riscv/src/cfg.h @@ -5,6 +5,7 @@ #include "sys.h" +uint32_t cfg_get_version (void); void cfg_update (uint32_t *args); void cfg_query (uint32_t *args); void cfg_init (void); diff --git a/sw/riscv/src/usb.c b/sw/riscv/src/usb.c index afd342f..e86c84f 100644 --- a/sw/riscv/src/usb.c +++ b/sw/riscv/src/usb.c @@ -222,6 +222,12 @@ void process_usb (void) { case STATE_DATA: switch (p.cmd) { + case 'V': + if (tx_word(cfg_get_version())) { + p.state = STATE_RESPONSE; + } + break; + case 'C': cfg_update(p.args); p.state = STATE_RESPONSE;