diff --git a/fw/rtl/cpu/cpu_usb.sv b/fw/rtl/cpu/cpu_usb.sv index 442a150..da439c2 100644 --- a/fw/rtl/cpu/cpu_usb.sv +++ b/fw/rtl/cpu/cpu_usb.sv @@ -20,6 +20,10 @@ module cpu_usb ( logic tx_write; logic [7:0] tx_wdata; + logic rx_escape_valid; + logic rx_escape_ack; + logic [7:0] rx_escape; + logic cpu_rx_read; logic cpu_tx_write; @@ -45,9 +49,10 @@ module cpu_usb ( always_comb begin bus.rdata = 32'd0; if (bus.ack) begin - case (bus.address[2:2]) - 0: bus.rdata = {26'd0, usb_pwren, usb_enabled, 2'b00, ~tx_full, ~rx_empty}; + case (bus.address[3:2]) + 0: bus.rdata = {25'd0, rx_escape_valid, usb_pwren, usb_enabled, 2'b00, ~tx_full, ~rx_empty}; 1: bus.rdata = {24'd0, rx_rdata}; + 2: bus.rdata = {24'd0, rx_escape}; default: bus.rdata = 32'd0; endcase end @@ -60,6 +65,8 @@ module cpu_usb ( tx_flush <= 1'b0; cpu_tx_write <= 1'b0; + rx_escape_ack <= 1'b0; + if (sys.reset) begin usb_enabled <= 1'b0; end else begin @@ -67,6 +74,7 @@ module cpu_usb ( case (bus.address[2:2]) 2'd0: begin if (bus.wmask[0]) begin + rx_escape_ack <= bus.wdata[7]; {usb_enabled, tx_flush, rx_flush} <= bus.wdata[4:2]; end end @@ -101,7 +109,11 @@ module cpu_usb ( .tx_flush(tx_flush), .tx_full(tx_full), .tx_write(tx_write), - .tx_wdata(tx_wdata) + .tx_wdata(tx_wdata), + + .rx_escape_valid(rx_escape_valid), + .rx_escape_ack(rx_escape_ack), + .rx_escape(rx_escape) ); endmodule diff --git a/fw/rtl/usb/usb_ft1248.sv b/fw/rtl/usb/usb_ft1248.sv index eb2720c..325991e 100644 --- a/fw/rtl/usb/usb_ft1248.sv +++ b/fw/rtl/usb/usb_ft1248.sv @@ -16,9 +16,15 @@ module usb_ft1248 ( input tx_flush, output tx_full, input tx_write, - input [7:0] tx_wdata + input [7:0] tx_wdata, + + output rx_escape_valid, + input rx_escape_ack, + output [7:0] rx_escape ); + parameter bit [7:0] ESCAPE_CHARACTER = 8'h1B; + // FIFOs logic rx_full; @@ -29,6 +35,9 @@ module usb_ft1248 ( logic tx_read; logic [7:0] tx_rdata; + logic rx_wdata_valid; + logic rx_escape_active; + intel_fifo_8 fifo_8_rx_inst ( .clock(sys.clk), .sclr(rx_flush || !usb_enabled), @@ -56,6 +65,41 @@ module usb_ft1248 ( ); + // Escape character detection + + always_comb begin + rx_write = 1'b0; + if (rx_wdata_valid) begin + rx_write = rx_escape_active ? rx_wdata == ESCAPE_CHARACTER : rx_wdata != ESCAPE_CHARACTER; + end + end + + always_ff @(posedge sys.clk) begin + if (sys.reset || !usb_enabled) begin + rx_escape_valid <= 1'b0; + rx_escape_active <= 1'b0; + end else begin + if (rx_escape_ack) begin + rx_escape_valid <= 1'b0; + end + + if (rx_wdata_valid) begin + if (!rx_escape_active) begin + if (rx_wdata == ESCAPE_CHARACTER) begin + rx_escape_active <= 1'b1; + end + end else begin + rx_escape_active <= 1'b0; + rx_escape <= rx_wdata; + if (rx_wdata != ESCAPE_CHARACTER) begin + rx_escape_valid <= 1'b1; + end + end + end + end + end + + // FT1248 interface controller typedef enum bit [1:0] { @@ -149,7 +193,7 @@ module usb_ft1248 ( end always_ff @(posedge sys.clk) begin - rx_write <= 1'b0; + rx_wdata_valid <= 1'b0; tx_read <= 1'b0; if (clock_phase[P_RISING]) begin @@ -161,7 +205,7 @@ module usb_ft1248 ( end else begin case (state) S_TRY_RX: begin - if (!rx_full) begin + if (!rx_full && !rx_escape_valid) begin state <= S_COMMAND; is_cmd_write <= 1'b0; nibble_counter <= 2'b11; @@ -202,9 +246,9 @@ module usb_ft1248 ( if (clock_phase[P_RISING]) begin rx_wdata <= {usb_miosi_input, rx_wdata[7:4]}; if (nibble_counter[0]) begin - rx_write <= !is_cmd_write; + rx_wdata_valid <= !is_cmd_write; end - if (usb_miso_input || (!is_cmd_write && rx_full) || (is_cmd_write && tx_empty)) begin + if (usb_miso_input || (!is_cmd_write && (rx_full || rx_escape_valid)) || (is_cmd_write && tx_empty)) begin state <= is_cmd_write ? S_TRY_RX : S_TRY_TX; end end diff --git a/sw/pc/dum.py b/sw/pc/dum.py deleted file mode 100644 index 1646da6..0000000 --- a/sw/pc/dum.py +++ /dev/null @@ -1,256 +0,0 @@ -from serial import Serial, SerialException -from serial.tools import list_ports -import argparse -import os -import sys -import time - - - -class SC64Exception(Exception): - pass - - - -class SC64: - __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 - - __SC64_VERSION_V2 = 0x53437632 - - __UPDATE_OFFSET = 0x03B60000 - - __CHUNK_SIZE = 1024 * 1024 - - __MIN_ROM_LENGTH = 0x101000 - __DDIPL_ROM_LENGTH = 0x400000 - - __serial = None - - - def __init__(self) -> None: - self.__find_sc64() - - - 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=5.0, write_timeout=5.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) - current_position = 0 - while (current_position < length): - chunk_size = min(self.__CHUNK_SIZE, length - current_position) - f.write(self.__read(chunk_size)) - current_position += chunk_size - 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) - while (f.tell() < length): - self.__write(f.read(min(self.__CHUNK_SIZE, length - f.tell()))) - 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, - 2: 2048, - 3: (32 * 1024), - 4: (128 * 1024), - 5: (3 * 32 * 1024), - 6: (128 * 1024), - 7: 0 - }[save_type] - - - 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) - saved_timeout = self.__serial.timeout - self.__serial.timeout = 20.0 - self.__change_config(self.__CFG_ID_FLASH_OPERATION, self.__UPDATE_OFFSET) - self.__serial.timeout = saved_timeout - 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 SC64Exception("Save type outside of supported values") - - - 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 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) - - - -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/requirements.txt b/sw/pc/requirements.txt new file mode 100644 index 0000000..decab53 --- /dev/null +++ b/sw/pc/requirements.txt @@ -0,0 +1,2 @@ +progressbar2==3.55.0 +pyserial==3.5 diff --git a/sw/pc/sc64.py b/sw/pc/sc64.py new file mode 100644 index 0000000..66c201e --- /dev/null +++ b/sw/pc/sc64.py @@ -0,0 +1,460 @@ +from serial import Serial, SerialException +from serial.tools import list_ports +import argparse +import os +import progressbar +import re +import sys +import time + + + +class SC64Exception(Exception): + pass + + + +class SC64: + __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 + + __SC64_VERSION_V2 = 0x53437632 + + __UPDATE_OFFSET = 0x03B60000 + + __CHUNK_SIZE = 256 * 1024 + + __MIN_ROM_LENGTH = 0x101000 + __DDIPL_ROM_LENGTH = 0x400000 + + + def __init__(self) -> None: + self.__serial = None + self.__progress_init = None + self.__progress_value = None + self.__progress_finish = None + self.__find_sc64() + + + def __del__(self) -> None: + if (self.__serial): + self.__serial.close() + + + def __set_progress_init(self, value: int, label: str) -> None: + if (self.__progress_init != None): + self.__progress_init(value, label) + + + def __set_progress_value(self, value: int) -> None: + if (self.__progress_value != None): + self.__progress_value(value) + + + def __set_progress_finish(self) -> None: + if (self.__progress_finish != None): + self.__progress_finish() + + + def __align(self, value: int, align: int) -> int: + return (value + (align - 1)) & ~(align - 1) + + + def __escape(self, data: bytes) -> bytes: + return re.sub(b'\x1B', b'\x1B\x1B', data) + + + def __reset_link(self) -> None: + self.__serial.write(b'\x1BR') + + + def __read(self, bytes: int) -> bytes: + return self.__serial.read(bytes) + + + def __write(self, data: bytes) -> None: + self.__serial.write(self.__escape(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) != f"CMP{cmd[0]}".encode(encoding="ascii")): + 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 + + if (self.__serial != None and self.__serial.isOpen()): + self.__serial.close() + + 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=1.0, write_timeout=1.0) + self.__serial.flushOutput() + self.__reset_link() + while (self.__serial.in_waiting): + self.__serial.read_all() + time.sleep(0.1) + self.__probe_device() + except (SerialException, 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.__set_progress_init(transfer_size, os.path.basename(f.name)) + self.__write_cmd("R", offset, transfer_size) + while (f.tell() < length): + chunk_size = min(self.__CHUNK_SIZE, length - f.tell()) + f.write(self.__read(chunk_size)) + self.__set_progress_value(f.tell()) + if (transfer_size != length): + self.__read(transfer_size - length) + self.__read_cmd_status("R") + self.__set_progress_finish() + + + 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.__set_progress_init(transfer_size, os.path.basename(f.name)) + self.__write_cmd("W", offset, transfer_size) + while (f.tell() < length): + self.__write(f.read(min(self.__CHUNK_SIZE, length - f.tell()))) + self.__set_progress_value(f.tell()) + if (transfer_size != length): + self.__write_dummy(transfer_size - length) + self.__read_cmd_status("W") + self.__set_progress_finish() + + + def __get_save_length(self) -> None: + save_type = self.__query_config(self.__CFG_ID_SAVE_TYPE) + return { + 0: 0, + 1: 512, + 2: 2048, + 3: (32 * 1024), + 4: (128 * 1024), + 5: (3 * 32 * 1024), + 6: (128 * 1024) + }[save_type] + + + 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 backup_firmware(self, file: str) -> None: + length = self.__query_config(self.__CFG_ID_FLASH_OPERATION, self.__UPDATE_OFFSET) + self.__read_file_from_sdram(file, self.__UPDATE_OFFSET, length) + + + def update_firmware(self, file: str) -> None: + self.__write_file_to_sdram(file, self.__UPDATE_OFFSET) + saved_timeout = self.__serial.timeout + self.__serial.timeout = 20.0 + self.__change_config(self.__CFG_ID_FLASH_OPERATION, self.__UPDATE_OFFSET) + self.__serial.timeout = saved_timeout + self.__reconfigure() + self.__find_sc64() + + + def set_skip_bootloader(self, enable: bool) -> None: + self.__change_config(self.__CFG_ID_SKIP_BOOTLOADER, 1 if enable else 0) + + + def set_tv_type(self, type: int = None) -> None: + if (type == None or type == 3): + self.__change_config(self.__CFG_ID_TV_TYPE, 3) + elif (type >= 0 and type <= 2): + self.__change_config(self.__CFG_ID_TV_TYPE, type) + else: + raise SC64Exception("TV type outside of supported values") + + + def get_tv_type_label(self, type: int) -> None: + if (type < 0 or type > 2): + return "Unknown" + return { + 0: "PAL", + 1: "NTSC", + 2: "MPAL" + }[type] + + + def set_cic_seed(self, type: int = None) -> None: + if (type == None): + self.__change_config(self.__CFG_ID_CIC_SEED, 0xFFFF) + else: + self.__change_config(self.__CFG_ID_CIC_SEED, type) + + + 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 SC64Exception("Save type outside of supported values") + + + def get_save_type_label(self, type: int) -> None: + if (type < 0 or type > 6): + return "Unknown" + return { + 0: "No save", + 1: "EEPROM 4Kb", + 2: "EEPROM 16Kb", + 3: "SRAM 256Kb", + 4: "FlashRAM 1Mb", + 5: "SRAM 768Kb", + 6: "FlashRAM 1Mb (Pokemon Stadium 2 special case)" + }[type] + + + 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 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) + + + +class SC64ProgressBar: + __LABEL_LENGTH = 30 + + + def __init__(self, sc64: SC64) -> None: + self.__sc64 = sc64 + self.__widgets = [ + "[ ", + progressbar.FormatLabel("{variables.label}", new_style=True), + " | ", + progressbar.Bar(left="", right=""), + " ", + progressbar.Percentage(), + " | ", + progressbar.DataSize(prefixes=(' ', 'Ki', 'Mi')), + " | ", + progressbar.AdaptiveTransferSpeed(), + " ]" + ] + self.__variables = {"label": ""} + self.__bar = None + + + def __enter__(self) -> None: + if (self.__sc64): + self.__sc64._SC64__progress_init = self.__progress_init + self.__sc64._SC64__progress_value = self.__progress_value + self.__sc64._SC64__progress_finish = self.__progress_finish + + + def __exit__(self, exc_type, exc_val, exc_tb) -> None: + if (self.__sc64): + self.__sc64._SC64__progress_init = None + self.__sc64._SC64__progress_value = None + self.__sc64._SC64__progress_finish = None + + + def __progress_init(self, value: int, label: str) -> None: + if (self.__bar == None): + self.__bar = progressbar.ProgressBar(widgets=self.__widgets, variables=self.__variables) + justified_label = label.ljust(self.__LABEL_LENGTH) + if (len(justified_label) > self.__LABEL_LENGTH): + justified_label = f"{justified_label[:self.__LABEL_LENGTH - 3]}..." + self.__bar.variables.label = justified_label + self.__bar.start(max_value=value) + + + def __progress_value(self, value: int) -> None: + self.__bar.update(value) + + + def __progress_finish(self) -> None: + self.__bar.finish(end="") + print() + + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='SummerCart64 one stop control center') + parser.add_argument('-b', default=False, action='store_true', required=False, help="skip internal bootloader") + parser.add_argument('-t', default="3", required=False, help='set TV type (0 - 2)') + parser.add_argument('-c', default="0xFFFF", required=False, help='set CIC seed') + parser.add_argument('-s', default="0", required=False, help='set save type (0 - 6)') + parser.add_argument('-d', default=False, action='store_true', required=False, help='enable 64DD emulation') + parser.add_argument('-r', default=False, action='store_true', required=False, help='perform reading operation instead of writing') + parser.add_argument('-l', default="0x101000", required=False, help='specify ROM length to read') + parser.add_argument('-u', default=None, required=False, help='path to update file') + parser.add_argument('-e', default=None, required=False, help='path to save file') + parser.add_argument('-i', default=None, required=False, help='path to DDIPL file') + parser.add_argument('rom', default=None, help='path to ROM file', nargs='?') + + args = parser.parse_args() + + if (len(sys.argv) <= 1): + parser.print_help() + parser.exit() + + try: + sc64 = SC64() + + skip_bootloader = args.b + save_type = int(args.s) + tv_type = int(args.t) + cic_seed = int(args.c, 0) + dd_enable = args.d + is_read = args.r + rom_length = int(args.l, 0) + update_file = args.u + save_file = args.e + dd_ipl_file = args.i + rom_file = args.rom + + with SC64ProgressBar(sc64): + if (update_file): + if (is_read): + sc64.backup_firmware(update_file) + else: + sc64.update_firmware(update_file) + + print(f"Setting save type to [{sc64.get_save_type_label(save_type)}]") + sc64.set_save_type(save_type) + + if (not is_read): + print(f"Setting skip internal bootloader to [{'True' if skip_bootloader else 'False'}]") + sc64.set_skip_bootloader(skip_bootloader) + + print(f"Setting TV type to [{sc64.get_tv_type_label(tv_type)}]") + sc64.set_tv_type(tv_type) + + print(f"Setting CIC seed to [0x{cic_seed:02X}]") + sc64.set_cic_seed(cic_seed) + + print(f"Setting 64DD emulation to [{'Enabled' if skip_bootloader else 'Disabled'}]") + sc64.set_dd_enable(dd_enable) + + if (rom_file): + if (is_read): + if (rom_length > 0): + sc64.download_rom(rom_file, rom_length) + else: + raise SC64Exception("ROM length should be larger than 0") + else: + sc64.upload_rom(rom_file) + + if (dd_ipl_file): + if (is_read): + sc64.download_dd_ipl(dd_ipl_file) + else: + sc64.upload_dd_ipl(dd_ipl_file) + + if (save_file): + if (is_read): + sc64.download_save(save_file) + else: + sc64.upload_save(save_file) + + except SC64Exception as e: + print(f"Error: {e}") + parser.exit(1) diff --git a/sw/riscv/src/sys.h b/sw/riscv/src/sys.h index 9b7db5b..008f04b 100644 --- a/sw/riscv/src/sys.h +++ b/sw/riscv/src/sys.h @@ -76,7 +76,9 @@ typedef volatile struct i2c_regs { typedef volatile struct usb_regs { io32_t SCR; io8_t DR; - io8_t __padding[3]; + io8_t __padding_1[3]; + io8_t ESCAPE; + io8_t __padding_2[3]; } usb_regs_t; #define USB_BASE (0x40000000UL) @@ -88,6 +90,8 @@ typedef volatile struct usb_regs { #define USB_SCR_FLUSH_TX (1 << 3) #define USB_SCR_ENABLED (1 << 4) #define USB_SCR_PWREN (1 << 5) +#define USB_SCR_ESCAPE_PENDING (1 << 6) +#define USB_SCR_ESCAPE_ACK (1 << 7) typedef volatile struct uart_regs { diff --git a/sw/riscv/src/usb.c b/sw/riscv/src/usb.c index e86c84f..d7713c3 100644 --- a/sw/riscv/src/usb.c +++ b/sw/riscv/src/usb.c @@ -13,18 +13,19 @@ static bool rx_byte (uint8_t *data) { return true; } +static uint8_t rx_word_current_byte = 0; +static uint32_t rx_word_buffer = 0; + static bool rx_word (uint32_t *data) { - static uint8_t current_byte = 0; - static uint32_t buffer = 0; uint8_t tmp; while (rx_byte(&tmp)) { - buffer = (buffer << 8) | tmp; - current_byte += 1; - if (current_byte == 4) { - current_byte = 0; - *data = buffer; - buffer = 0; + rx_word_buffer = (rx_word_buffer << 8) | tmp; + rx_word_current_byte += 1; + if (rx_word_current_byte == 4) { + rx_word_current_byte = 0; + *data = rx_word_buffer; + rx_word_buffer = 0; return true; } @@ -43,12 +44,13 @@ static bool tx_byte (uint8_t data) { return true; } +static uint8_t tx_word_current_byte = 0; + static bool tx_word (uint32_t data) { - static uint8_t current_byte = 0; - while (tx_byte(data >> ((3 - current_byte) * 8))) { - current_byte += 1; - if (current_byte == 4) { - current_byte = 0; + while (tx_byte(data >> ((3 - tx_word_current_byte) * 8))) { + tx_word_current_byte += 1; + if (tx_word_current_byte == 4) { + tx_word_current_byte = 0; return true; } @@ -152,25 +154,25 @@ void usb_debug_reset (void) { USB->SCR = USB_SCR_FLUSH_TX | USB_SCR_FLUSH_RX; } +static uint8_t rx_cmd_current_byte = 0; +static uint32_t rx_cmd_buffer = 0; static bool rx_cmd (uint32_t *data) { - static uint8_t current_byte = 0; - static uint32_t buffer = 0; uint8_t tmp; while (rx_byte(&tmp)) { - current_byte += 1; - if ((current_byte != 4) && (tmp != (USB_CMD_TOKEN >> (8 * (4 - current_byte)) & 0xFF))) { - current_byte = 0; - buffer = 0; + rx_cmd_current_byte += 1; + if ((rx_cmd_current_byte != 4) && (tmp != (USB_CMD_TOKEN >> (8 * (4 - rx_cmd_current_byte)) & 0xFF))) { + rx_cmd_current_byte = 0; + rx_cmd_buffer = 0; return false; } - buffer = (buffer << 8) | tmp; - if (current_byte == 4) { - current_byte = 0; - *data = buffer; - buffer = 0; + rx_cmd_buffer = (rx_cmd_buffer << 8) | tmp; + if (rx_cmd_current_byte == 4) { + rx_cmd_current_byte = 0; + *data = rx_cmd_buffer; + rx_cmd_buffer = 0; return true; } @@ -179,6 +181,19 @@ static bool rx_cmd (uint32_t *data) { return false; } +static void handle_escape (void) { + if (USB->SCR & USB_SCR_ESCAPE_PENDING) { + if (USB->ESCAPE == 'R') { + if (p.dma_in_progress) { + dma_stop(); + while (dma_busy()); + } + usb_init(); + } + USB->SCR |= USB_SCR_ESCAPE_ACK; + } +} + void usb_init (void) { USB->SCR = USB_SCR_ENABLED | USB_SCR_FLUSH_TX | USB_SCR_FLUSH_RX; @@ -186,10 +201,18 @@ void usb_init (void) { p.state = STATE_IDLE; p.debug_rx_busy = false; p.debug_tx_busy = false; + + rx_word_current_byte = 0; + rx_word_buffer = 0; + tx_word_current_byte = 0; + rx_cmd_current_byte = 0; + rx_cmd_buffer = 0; } void process_usb (void) { + handle_escape(); + switch (p.state) { case STATE_IDLE: if (p.debug_tx_busy) {