diff --git a/fw/ftdi/ft232h_config.xml b/fw/ftdi/ft232h_config.xml index 664dde4..30ed587 100644 Binary files a/fw/ftdi/ft232h_config.xml and b/fw/ftdi/ft232h_config.xml differ diff --git a/fw/project/lcmxo2/sc64.ldf b/fw/project/lcmxo2/sc64.ldf index 9bdab48..7b10953 100644 --- a/fw/project/lcmxo2/sc64.ldf +++ b/fw/project/lcmxo2/sc64.ldf @@ -42,6 +42,9 @@ + + + diff --git a/fw/rtl/mcu/mcu_top.sv b/fw/rtl/mcu/mcu_top.sv index e4fa0df..50bb33c 100644 --- a/fw/rtl/mcu/mcu_top.sv +++ b/fw/rtl/mcu/mcu_top.sv @@ -607,6 +607,7 @@ module mcu_top ( sd_dma_scb.stop <= 1'b0; n64_scb.cfg_done <= 1'b0; + n64_scb.cfg_error <= 1'b0; n64_scb.cfg_irq <= 1'b0; n64_scb.flashram_done <= 1'b0; diff --git a/fw/rtl/n64/n64_cfg.sv b/fw/rtl/n64/n64_cfg.sv index a89f6a1..3b13546 100644 --- a/fw/rtl/n64/n64_cfg.sv +++ b/fw/rtl/n64/n64_cfg.sv @@ -20,21 +20,25 @@ module n64_cfg ( REG_VERSION_L } e_reg; + logic cfg_error; + always_comb begin reg_bus.rdata = 16'd0; - case (reg_bus.address[3:1]) - REG_STATUS: reg_bus.rdata = { - n64_scb.cfg_pending, - n64_scb.cfg_error, - 14'd0 - }; - REG_DATA_0_H: reg_bus.rdata = n64_scb.cfg_wdata[0][31:16]; - REG_DATA_0_L: reg_bus.rdata = n64_scb.cfg_wdata[0][15:0]; - REG_DATA_1_H: reg_bus.rdata = n64_scb.cfg_wdata[1][31:16]; - REG_DATA_1_L: reg_bus.rdata = n64_scb.cfg_wdata[1][15:0]; - REG_VERSION_H: reg_bus.rdata = n64_scb.cfg_version[31:16]; - REG_VERSION_L: reg_bus.rdata = n64_scb.cfg_version[15:0]; - endcase + if (reg_bus.address[16] && (reg_bus.address[15:4] == 12'd0)) begin + case (reg_bus.address[3:1]) + REG_STATUS: reg_bus.rdata = { + n64_scb.cfg_pending, + cfg_error, + 14'd0 + }; + REG_DATA_0_H: reg_bus.rdata = n64_scb.cfg_wdata[0][31:16]; + REG_DATA_0_L: reg_bus.rdata = n64_scb.cfg_wdata[0][15:0]; + REG_DATA_1_H: reg_bus.rdata = n64_scb.cfg_wdata[1][31:16]; + REG_DATA_1_L: reg_bus.rdata = n64_scb.cfg_wdata[1][15:0]; + REG_VERSION_H: reg_bus.rdata = n64_scb.cfg_version[31:16]; + REG_VERSION_L: reg_bus.rdata = n64_scb.cfg_version[15:0]; + endcase + end end always_ff @(posedge clk) begin @@ -42,9 +46,11 @@ module n64_cfg ( n64_scb.cfg_pending <= 1'b0; n64_scb.cfg_cmd <= 8'h00; irq <= 1'b0; + cfg_error <= 1'b0; end else begin if (n64_scb.cfg_done) begin n64_scb.cfg_pending <= 1'b0; + cfg_error <= n64_scb.cfg_error; end if (n64_scb.cfg_irq) begin @@ -52,17 +58,20 @@ module n64_cfg ( end if (reg_bus.write) begin - case (reg_bus.address[3:1]) - REG_COMMAND: begin - n64_scb.cfg_pending <= 1'b1; - n64_scb.cfg_cmd <= reg_bus.wdata[7:0]; - end - REG_DATA_0_H: n64_scb.cfg_rdata[0][31:16] <= reg_bus.wdata; - REG_DATA_0_L: n64_scb.cfg_rdata[0][15:0] <= reg_bus.wdata; - REG_DATA_1_H: n64_scb.cfg_rdata[1][31:16] <= reg_bus.wdata; - REG_DATA_1_L: n64_scb.cfg_rdata[1][15:0] <= reg_bus.wdata; - REG_VERSION_L: irq <= 1'b0; - endcase + if (reg_bus.address[16] && (reg_bus.address[15:4] == 12'd0)) begin + case (reg_bus.address[3:1]) + REG_COMMAND: begin + n64_scb.cfg_pending <= 1'b1; + n64_scb.cfg_cmd <= reg_bus.wdata[7:0]; + cfg_error <= 1'b0; + end + REG_DATA_0_H: n64_scb.cfg_rdata[0][31:16] <= reg_bus.wdata; + REG_DATA_0_L: n64_scb.cfg_rdata[0][15:0] <= reg_bus.wdata; + REG_DATA_1_H: n64_scb.cfg_rdata[1][31:16] <= reg_bus.wdata; + REG_DATA_1_L: n64_scb.cfg_rdata[1][15:0] <= reg_bus.wdata; + REG_VERSION_L: irq <= 1'b0; + endcase + end end end end diff --git a/fw/rtl/n64/n64_lock.sv b/fw/rtl/n64/n64_lock.sv new file mode 100644 index 0000000..5679260 --- /dev/null +++ b/fw/rtl/n64/n64_lock.sv @@ -0,0 +1,52 @@ +module n64_lock ( + input clk, + input reset, + + n64_reg_bus.lock reg_bus, + + n64_scb.lock n64_scb, +); + + const bit [15:0] UNLOCK_SEQUENCE [4] = { + 16'h5F55, + 16'h4E4C, + 16'h4F43, + 16'h4B5F + }; + + always_comb begin + reg_bus.rdata = 16'd0; + end + + logic [1:0] sequence_counter; + + always_ff @(posedge clk) begin + if (reset || n64_scb.n64_reset || n64_scb.n64_nmi) begin + n64_scb.cfg_unlock <= 1'b0; + sequence_counter <= 2'd0; + end else begin + if (reg_bus.write) begin + if (reg_bus.address[16] && (reg_bus.address[15:2] == 14'd0)) begin + for (int i = 0; i < $size(UNLOCK_SEQUENCE); i++) begin + if (sequence_counter == i) begin + if (reg_bus.wdata == UNLOCK_SEQUENCE[i]) begin + sequence_counter <= sequence_counter + 1'd1; + if (i == ($size(UNLOCK_SEQUENCE) - 1'd1)) begin + n64_scb.cfg_unlock <= 1'b1; + sequence_counter <= 2'd0; + end + end else begin + n64_scb.cfg_unlock <= 1'b0; + sequence_counter <= 2'd0; + end + end + end + end else begin + n64_scb.cfg_unlock <= 1'b0; + sequence_counter <= 2'd0; + end + end + end + end + +endmodule diff --git a/fw/rtl/n64/n64_pi.sv b/fw/rtl/n64/n64_pi.sv index 025bc07..924e750 100644 --- a/fw/rtl/n64/n64_pi.sv +++ b/fw/rtl/n64/n64_pi.sv @@ -139,6 +139,7 @@ module n64_pi ( write_port <= PORT_NONE; reg_bus.dd_select <= 1'b0; reg_bus.flashram_select <= 1'b0; + reg_bus.lock_select <= 1'b0; reg_bus.cfg_select <= 1'b0; end else if (aleh_op) begin read_port <= PORT_NONE; @@ -146,6 +147,7 @@ module n64_pi ( mem_offset <= 32'd0; reg_bus.dd_select <= 1'b0; reg_bus.flashram_select <= 1'b0; + reg_bus.lock_select <= 1'b0; reg_bus.cfg_select <= 1'b0; if (n64_scb.dd_enabled) begin @@ -220,16 +222,24 @@ module n64_pi ( mem_offset <= (-32'h1400_0000) + FLASH_OFFSET; end - if (n64_pi_dq_in >= 16'h1FFE && n64_pi_dq_in < 16'h1FFF) begin - read_port <= PORT_MEM; - write_port <= PORT_MEM; - mem_offset <= (-32'h1FFE_0000) + BUFFER_OFFSET; + if (n64_pi_dq_in >= 16'h1FFD && n64_pi_dq_in < 16'h1FFE) begin + read_port <= PORT_NONE; + write_port <= PORT_REG; + reg_bus.lock_select <= 1'b1; end - if (n64_pi_dq_in >= 16'h1FFF && n64_pi_dq_in < 16'h2000) begin - read_port <= PORT_REG; - write_port <= PORT_REG; - reg_bus.cfg_select <= 1'b1; + if (n64_scb.cfg_unlock) begin + if (n64_pi_dq_in >= 16'h1FFE && n64_pi_dq_in < 16'h1FFF) begin + read_port <= PORT_MEM; + write_port <= PORT_MEM; + mem_offset <= (-32'h1FFE_0000) + BUFFER_OFFSET; + end + + if (n64_pi_dq_in >= 16'h1FFF && n64_pi_dq_in < 16'h2000) begin + read_port <= PORT_REG; + write_port <= PORT_REG; + reg_bus.cfg_select <= 1'b1; + end end end end diff --git a/fw/rtl/n64/n64_reg_bus.sv b/fw/rtl/n64/n64_reg_bus.sv index 07573b7..969edd5 100644 --- a/fw/rtl/n64/n64_reg_bus.sv +++ b/fw/rtl/n64/n64_reg_bus.sv @@ -2,6 +2,7 @@ interface n64_reg_bus (); logic flashram_select; logic dd_select; + logic lock_select; logic cfg_select; logic read; @@ -12,12 +13,14 @@ interface n64_reg_bus (); logic [15:0] flashram_rdata; logic [15:0] dd_rdata; + logic [15:0] lock_rdata; logic [15:0] cfg_rdata; modport controller ( output flashram_select, output dd_select, output cfg_select, + output lock_select, output read, output write, @@ -34,6 +37,9 @@ interface n64_reg_bus (); if (dd_select) begin rdata = dd_rdata; end + if (lock_select) begin + rdata = lock_rdata; + end if (cfg_select) begin rdata = cfg_rdata; end @@ -55,6 +61,14 @@ interface n64_reg_bus (); input wdata ); + modport lock ( + input .read(read && lock_select), + input .write(write && lock_select), + input address, + output .rdata(lock_rdata), + input wdata + ); + modport cfg ( input .read(read && cfg_select), input .write(write && cfg_select), diff --git a/fw/rtl/n64/n64_scb.sv b/fw/rtl/n64/n64_scb.sv index b961435..9cf6d3f 100644 --- a/fw/rtl/n64/n64_scb.sv +++ b/fw/rtl/n64/n64_scb.sv @@ -40,6 +40,7 @@ interface n64_scb (); logic [41:0] rtc_rdata; logic [41:0] rtc_wdata; + logic cfg_unlock; logic cfg_pending; logic cfg_done; logic cfg_error; @@ -99,7 +100,9 @@ interface n64_scb (); input dd_enabled, input ddipl_enabled, - input flashram_read_mode + input flashram_read_mode, + + input cfg_unlock ); modport flashram ( @@ -158,6 +161,13 @@ interface n64_scb (); input dd_wdata ); + modport lock ( + input n64_reset, + input n64_nmi, + + output cfg_unlock + ); + modport cfg ( input n64_reset, diff --git a/fw/rtl/n64/n64_top.sv b/fw/rtl/n64/n64_top.sv index c63eafc..f6fd911 100644 --- a/fw/rtl/n64/n64_top.sv +++ b/fw/rtl/n64/n64_top.sv @@ -67,6 +67,15 @@ module n64_top ( .n64_scb(n64_scb) ); + n64_lock n64_lock_inst ( + .clk(clk), + .reset(reset), + + .reg_bus(reg_bus), + + .n64_scb(n64_scb) + ); + n64_cfg n64_cfg_inst ( .clk(clk), .reset(reset), diff --git a/sw/bootloader/src/init.c b/sw/bootloader/src/init.c index a519255..a2e38dc 100644 --- a/sw/bootloader/src/init.c +++ b/sw/bootloader/src/init.c @@ -11,6 +11,8 @@ void init (void) { exception_install(); + sc64_unlock(); + if (!sc64_check_presence()) { error_display("SC64 hardware not detected"); } @@ -29,4 +31,5 @@ void init (void) { void deinit (void) { exception_disable_interrupts(); exception_disable_watchdog(); + sc64_lock(); } diff --git a/sw/bootloader/src/io.h b/sw/bootloader/src/io.h index d289d3c..512a4ea 100644 --- a/sw/bootloader/src/io.h +++ b/sw/bootloader/src/io.h @@ -225,6 +225,14 @@ typedef struct { #define PIFRAM ((io8_t *) PIFRAM_BASE) +typedef struct { + io32_t KEY; +} sc64_lock_t; + +#define SC64_LOCK_BASE (0x1FFD0000UL) +#define SC64_LOCK ((sc64_lock_t *) SC64_LOCK_BASE) + + typedef struct { io8_t BUFFER[8192]; io8_t EEPROM[2048]; diff --git a/sw/bootloader/src/sc64.c b/sw/bootloader/src/sc64.c index cd3c779..ce2a047 100644 --- a/sw/bootloader/src/sc64.c +++ b/sw/bootloader/src/sc64.c @@ -40,6 +40,16 @@ static bool sc64_execute_cmd (uint8_t cmd, uint32_t *args, uint32_t *result) { return error; } +void sc64_unlock (void) { + pi_io_write(&SC64_LOCK->KEY, 0x00000000); + pi_io_write(&SC64_LOCK->KEY, 0x5F554E4C); + pi_io_write(&SC64_LOCK->KEY, 0x4F434B5F); +} + +void sc64_lock (void) { + pi_io_write(&SC64_LOCK->KEY, 0x00000000); +} + bool sc64_check_presence (void) { uint32_t version = pi_io_read(&SC64_REGS->VERSION); return (version == SC64_VERSION_2); diff --git a/sw/bootloader/src/sc64.h b/sw/bootloader/src/sc64.h index f9c4c8c..3d24b3b 100644 --- a/sw/bootloader/src/sc64.h +++ b/sw/bootloader/src/sc64.h @@ -91,6 +91,8 @@ typedef struct { } rtc_time_t; +void sc64_unlock (void); +void sc64_lock (void); bool sc64_check_presence (void); cmd_error_t sc64_get_error (void); void sc64_set_config (cfg_id_t id, uint32_t value); diff --git a/sw/controller/src/button.c b/sw/controller/src/button.c index f92e5c6..770b2a9 100644 --- a/sw/controller/src/button.c +++ b/sw/controller/src/button.c @@ -54,7 +54,7 @@ void button_process (void) { if (p.trigger) { switch (p.mode) { case BUTTON_MODE_N64_IRQ: - fpga_reg_set(REG_CFG_CMD, fpga_reg_get(REG_CFG_CMD) | CFG_CMD_IRQ); + fpga_reg_set(REG_CFG_CMD, CFG_CMD_IRQ); p.trigger = false; break; diff --git a/sw/pc/sc64.py b/sw/pc/sc64.py index c705a57..19de8c4 100755 --- a/sw/pc/sc64.py +++ b/sw/pc/sc64.py @@ -11,7 +11,7 @@ from dd64 import BadBlockError, DD64Image from enum import Enum, IntEnum from serial.tools import list_ports from threading import Thread -from typing import Optional +from typing import Callable, Optional @@ -394,8 +394,6 @@ class SC64: def upload_save(self, data: bytes) -> None: save_type = self.SaveType(self.__get_config(self.__CfgId.SAVE_TYPE)) - if (save_type not in self.SaveType): - raise ConnectionError('Unknown save type fetched from SC64 device') if (save_type == self.SaveType.NONE): raise ValueError('No save type set inside SC64 device') if (len(data) != self.__SaveLength[save_type.name]): @@ -407,8 +405,6 @@ class SC64: def download_save(self) -> bytes: save_type = self.SaveType(self.__get_config(self.__CfgId.SAVE_TYPE)) - if (save_type not in self.SaveType): - raise ConnectionError('Unknown save type fetched from SC64 device') if (save_type == self.SaveType.NONE): raise ValueError('No save type set inside SC64 device') address = self.__Address.SAVE @@ -432,8 +428,6 @@ class SC64: self.__link.execute_cmd(cmd=b'T', args=[self.__get_int(data[0:4]), self.__get_int(data[4:8])]) def set_boot_mode(self, mode: BootMode) -> None: - if (mode not in self.BootMode): - raise ValueError('Boot mode outside of allowed values') self.__set_config(self.__CfgId.BOOT_MODE, mode) def set_cic_seed(self, seed: int) -> None: @@ -443,13 +437,9 @@ class SC64: self.__set_config(self.__CfgId.CIC_SEED, seed) def set_tv_type(self, type: TVType) -> None: - if (type not in self.TVType): - raise ValueError('TV type outside of allowed values') self.__set_config(self.__CfgId.TV_TYPE, type) def set_save_type(self, type: SaveType) -> None: - if (type not in self.SaveType): - raise ValueError('Save type outside of allowed values') self.__set_config(self.__CfgId.SAVE_TYPE, type) def set_cic_parameters(self, dd_mode: bool=False, seed: int=0x3F, checksum: bytes=bytes([0xA5, 0x36, 0xC0, 0xF1, 0xD8, 0x59])) -> None: @@ -461,7 +451,7 @@ class SC64: data = [*data, *checksum] self.__link.execute_cmd(cmd=b'B', args=[self.__get_int(data[0:4]), self.__get_int(data[4:8])]) - def update_firmware(self, data: bytes) -> None: + def update_firmware(self, data: bytes, status_callback: Optional[Callable[[str], None]]=None) -> None: address = self.__Address.FIRMWARE self.__write_memory(address, data) response = self.__link.execute_cmd(cmd=b'F', args=[address, len(data)]) @@ -477,7 +467,8 @@ class SC64: if (cmd != b'F'): raise ConnectionException('Wrong update status packet') status = self.__UpdateStatus(self.__get_int(data)) - print(f'Update status [{status.name}]') + if (status_callback): + status_callback(status.name) if (status == self.__UpdateStatus.ERROR): raise ConnectionException('Update error, device is most likely bricked') time.sleep(2) @@ -616,8 +607,8 @@ class EnumAction(argparse.Action): if __name__ == '__main__': parser = argparse.ArgumentParser(description='SC64 control software') - parser.add_argument('--backup', help='backup SC64 firmware and write it to specified file') - parser.add_argument('--update', help='update SC64 firmware from specified file') + parser.add_argument('--backup-firmware', help='backup SC64 firmware and write it to specified file') + parser.add_argument('--update-firmware', help='update SC64 firmware from specified file') parser.add_argument('--reset-state', action='store_true', help='reset SC64 internal state') parser.add_argument('--print-state', action='store_true', help='print SC64 internal state') parser.add_argument('--boot', type=SC64.BootMode, action=EnumAction, help='set boot mode') @@ -632,7 +623,7 @@ if __name__ == '__main__': parser.add_argument('--ddipl', help='upload 64DD IPL from specified file') parser.add_argument('--disk', action='append', help='path to 64DD disk (.ndd format), can be specified multiple times') parser.add_argument('--isv', action='store_true', help='enable IS-Viewer64 support') - parser.add_argument('--debug', action='store_true', help='run debug loop (required for IS-Viewer64 and 64DD)') + parser.add_argument('--debug', action='store_true', help='run debug loop (required for 64DD and IS-Viewer64)') if (len(sys.argv) <= 1): parser.print_help() @@ -643,16 +634,17 @@ if __name__ == '__main__': try: sc64 = SC64() - if (args.backup): - with open(args.backup, 'wb+') as f: + if (args.backup_firmware): + with open(args.backup_firmware, 'wb+') as f: print('Generating backup, this might take a while... ', end='', flush=True) f.write(sc64.backup_firmware()) print('done') - if (args.update): - with open(args.update, 'rb+') as f: + if (args.update_firmware): + with open(args.update_firmware, 'rb+') as f: print('Updating firmware, this might take a while... ', end='', flush=True) - sc64.update_firmware(f.read()) + status_callback = lambda status: print(f'{status} ', end='', flush=True) + sc64.update_firmware(f.read(), status_callback) print('done') if (args.reset_state):