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):