From 92e5c5747bd44989a9165c9c1dd45092a32695af Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Fri, 24 Dec 2021 23:51:30 +0100 Subject: [PATCH] [SC64][FW][SW] Added 64DD implementation with USB streaming (#14) --- fw/SummerCart64.qsf | 1 + fw/rtl/SummerCart64.sv | 13 +- fw/rtl/cpu/cpu_dd.sv | 141 +++++++++++++ fw/rtl/cpu/cpu_soc.sv | 7 + fw/rtl/n64/n64_bus.sv | 15 +- fw/rtl/n64/n64_dd.sv | 389 ++++++++++++++++++++++++++++++++---- fw/rtl/n64/n64_pi.sv | 22 +++ fw/rtl/n64/n64_soc.sv | 9 +- fw/rtl/system/sc64.sv | 1 + sw/n64/src/boot.c | 29 +-- sw/n64/src/main.c | 2 +- sw/pc/sc64.py | 258 +++++++++++++++++++++--- sw/riscv/Makefile | 2 +- sw/riscv/src/cfg.c | 43 +++- sw/riscv/src/dd.c | 440 +++++++++++++++++++++++++++++++++++++++++ sw/riscv/src/dd.h | 23 +++ sw/riscv/src/process.c | 3 + sw/riscv/src/sys.h | 56 ++++++ sw/riscv/src/usb.c | 2 +- 19 files changed, 1371 insertions(+), 85 deletions(-) create mode 100644 fw/rtl/cpu/cpu_dd.sv create mode 100644 sw/riscv/src/dd.c create mode 100644 sw/riscv/src/dd.h diff --git a/fw/SummerCart64.qsf b/fw/SummerCart64.qsf index d2199ee..d24b06c 100644 --- a/fw/SummerCart64.qsf +++ b/fw/SummerCart64.qsf @@ -54,6 +54,7 @@ set_global_assignment -name SDC_FILE SummerCart64.sdc set_global_assignment -name SYSTEMVERILOG_FILE picorv32/picorv32.v set_global_assignment -name SYSTEMVERILOG_FILE rtl/cpu/cpu_bus.sv set_global_assignment -name SYSTEMVERILOG_FILE rtl/cpu/cpu_cfg.sv +set_global_assignment -name SYSTEMVERILOG_FILE rtl/cpu/cpu_dd.sv set_global_assignment -name SYSTEMVERILOG_FILE rtl/cpu/cpu_dma.sv set_global_assignment -name SYSTEMVERILOG_FILE rtl/cpu/cpu_flash.sv set_global_assignment -name SYSTEMVERILOG_FILE rtl/cpu/cpu_flashram.sv diff --git a/fw/rtl/SummerCart64.sv b/fw/rtl/SummerCart64.sv index 16c5769..cb164c7 100644 --- a/fw/rtl/SummerCart64.sv +++ b/fw/rtl/SummerCart64.sv @@ -46,6 +46,8 @@ module SummerCart64 ( logic [7:0] gpio_i; logic [7:0] gpio_oe; + logic dd_interrupt; + if_system sys ( .in_clk(i_clk), .n64_reset(i_n64_reset), @@ -64,6 +66,10 @@ module SummerCart64 ( if_flash flash (); + if_dd dd ( + .dd_interrupt(dd_interrupt) + ); + system system_inst ( .sys(sys) ); @@ -82,6 +88,7 @@ module SummerCart64 ( .flashram(flashram), .si(si), .flash(flash), + .dd(dd), .n64_pi_alel(i_n64_pi_alel), .n64_pi_aleh(i_n64_pi_aleh), @@ -109,6 +116,7 @@ module SummerCart64 ( .flashram(flashram), .si(si), .flash(flash), + .dd(dd), .gpio_o(gpio_o), .gpio_i(gpio_i), @@ -131,9 +139,12 @@ module SummerCart64 ( .sd_dat(io_sd_dat) ); + always_comb begin + o_n64_irq = dd_interrupt ? 1'b0 : 1'bZ; + end + always_comb begin o_led = gpio_oe[0] ? gpio_o[0] : 1'bZ; - o_n64_irq = gpio_oe[1] ? gpio_o[1] : 1'bZ; end always_ff @(posedge sys.clk) begin diff --git a/fw/rtl/cpu/cpu_dd.sv b/fw/rtl/cpu/cpu_dd.sv new file mode 100644 index 0000000..f4227d2 --- /dev/null +++ b/fw/rtl/cpu/cpu_dd.sv @@ -0,0 +1,141 @@ +module cpu_dd ( + if_system.sys sys, + if_cpu_bus bus, + if_dd.cpu dd +); + + const bit [8:0] M_SECTOR_BUFFER = 9'h100; + + logic bm_ack; + logic [31:0] seek_timer; + + typedef enum bit [2:0] { + R_SCR, + R_CMD_DATA, + R_HEAD_TRACK, + R_SECTOR_INFO, + R_DRIVE_ID, + R_SEEK_TIMER + } e_reg_id; + + always_ff @(posedge sys.clk) begin + bus.ack <= 1'b0; + if (bus.request) begin + bus.ack <= 1'b1; + end + end + + always_comb begin + bus.rdata = 32'd0; + if (bus.ack) begin + if (bus.address[8] == M_SECTOR_BUFFER[8]) begin + bus.rdata = { + dd.sector_rdata[7:0], + dd.sector_rdata[15:8], + dd.sector_rdata[23:16], + dd.sector_rdata[31:24] + }; + end else begin + case (bus.address[5:2]) + R_SCR: bus.rdata = { + 14'd0, + bm_ack, + dd.bm_micro_error, + dd.bm_transfer_c2, + dd.bm_transfer_data, + dd.bm_transfer_blocks, + dd.bm_transfer_mode, + 1'b0, + dd.bm_stop_pending, + 1'b0, + dd.bm_start_pending, + dd.disk_changed, + dd.disk_inserted, + 1'b0, + dd.bm_pending, + 1'b0, + dd.cmd_pending, + 1'b0, + dd.hard_reset + }; + R_CMD_DATA: bus.rdata = {8'd0, dd.cmd, dd.data}; + R_HEAD_TRACK: bus.rdata = {18'd0, dd.index_lock, dd.head_track}; + R_SECTOR_INFO: bus.rdata = { + dd.sectors_in_block, + dd.sector_size_full, + dd.sector_size, + dd.sector_num + }; + R_DRIVE_ID: bus.rdata = {dd.drive_id}; + R_SEEK_TIMER: bus.rdata = seek_timer; + default: bus.rdata = 32'd0; + endcase + end + end + end + + always_comb begin + dd.sector_address = bus.address[7:2]; + dd.sector_address_valid = bus.request && bus.address[8] == M_SECTOR_BUFFER[8]; + dd.sector_write = (&bus.wmask) && dd.sector_address_valid; + dd.sector_wdata = {bus.wdata[7:0], bus.wdata[15:8], bus.wdata[23:16], bus.wdata[31:24]}; + end + + always_ff @(posedge sys.clk) begin + dd.hard_reset_clear <= 1'b0; + dd.cmd_ready <= 1'b0; + dd.bm_start_clear <= 1'b0; + dd.bm_stop_clear <= 1'b0; + dd.bm_clear <= 1'b0; + dd.bm_ready <= 1'b0; + + if (dd.bm_interrupt_ack) begin + bm_ack <= 1'b1; + end + + if (!(&seek_timer)) begin + seek_timer <= seek_timer + 1'd1; + end + + if (sys.reset) begin + bm_ack <= 1'b0; + end else begin + if (bus.request && (!bus.address[8])) begin + case (bus.address[4:2]) + R_SCR: if (&bus.wmask) begin + if (bus.wdata[20]) begin + seek_timer <= 32'd0; + end + dd.bm_clear <= bus.wdata[19]; + if (bus.wdata[18]) begin + bm_ack <= 1'b0; + end + dd.bm_micro_error <= bus.wdata[16]; + dd.bm_transfer_c2 <= bus.wdata[15]; + dd.bm_transfer_data <= bus.wdata[14]; + dd.bm_stop_clear <= bus.wdata[11]; + dd.bm_start_clear <= bus.wdata[9]; + dd.disk_changed <= bus.wdata[7]; + dd.disk_inserted <= bus.wdata[6]; + dd.bm_ready <= bus.wdata[5]; + dd.cmd_ready <= bus.wdata[3]; + dd.hard_reset_clear <= bus.wdata[1]; + end + + R_CMD_DATA: if (&bus.wmask[1:0]) begin + dd.cmd_data <= bus.wdata[15:0]; + end + + R_HEAD_TRACK: if (&bus.wmask[1:0]) begin + {dd.index_lock, dd.head_track} <= bus.wdata[13:0]; + end + + R_DRIVE_ID: if (&bus.wmask[1:0]) begin + dd.drive_id <= bus.wdata[15:0]; + end + endcase + end + end + end + +endmodule diff --git a/fw/rtl/cpu/cpu_soc.sv b/fw/rtl/cpu/cpu_soc.sv index 14e82bd..7a15039 100644 --- a/fw/rtl/cpu/cpu_soc.sv +++ b/fw/rtl/cpu/cpu_soc.sv @@ -6,6 +6,7 @@ module cpu_soc ( if_flashram.cpu flashram, if_si.cpu si, if_flash.cpu flash, + if_dd.cpu dd, input [7:0] gpio_i, output [7:0] gpio_o, @@ -113,6 +114,12 @@ module cpu_soc ( .si(si) ); + cpu_dd cpu_dd_inst ( + .sys(sys), + .bus(bus.at[sc64::ID_CPU_DD].device), + .dd(dd) + ); + assign sd_clk = 1'bZ; assign sd_cmd = 1'bZ; assign sd_dat = 4'bZZZZ; diff --git a/fw/rtl/n64/n64_bus.sv b/fw/rtl/n64/n64_bus.sv index c4080f8..e3c90aa 100644 --- a/fw/rtl/n64/n64_bus.sv +++ b/fw/rtl/n64/n64_bus.sv @@ -9,6 +9,9 @@ interface if_n64_bus (); logic [31:0] address; logic [15:0] wdata; logic [15:0] rdata; + logic [31:0] real_address; + logic read_op; + logic write_op; logic device_ack [(NUM_DEVICES - 1):0]; logic [15:0] device_rdata [(NUM_DEVICES - 1):0]; @@ -30,7 +33,11 @@ interface if_n64_bus (); output write, output address, output wdata, - input rdata + input rdata, + + output real_address, + output read_op, + output write_op ); genvar n; @@ -48,7 +55,11 @@ interface if_n64_bus (); input .write(write), input .address(address), input .wdata(wdata), - output .rdata(device_rdata[n]) + output .rdata(device_rdata[n]), + + input .real_address(real_address), + input .read_op(read_op), + input .write_op(write_op) ); end endgenerate diff --git a/fw/rtl/n64/n64_dd.sv b/fw/rtl/n64/n64_dd.sv index 51e5812..2c36d48 100644 --- a/fw/rtl/n64/n64_dd.sv +++ b/fw/rtl/n64/n64_dd.sv @@ -1,40 +1,158 @@ -interface if_dd(); +interface if_dd ( + output dd_interrupt +); + + // Sector buffer regs + + logic [6:0] n64_sector_address; + logic n64_sector_address_valid; + logic n64_sector_write; + logic [15:0] n64_sector_wdata; + + logic [5:0] cpu_sector_address; + logic cpu_sector_address_valid; + logic cpu_sector_write; + logic [31:0] cpu_sector_wdata; + + logic [31:0] sector_rdata; + + + // N64 controlled regs logic hard_reset; - logic cmd_request; - logic cmd_ack; - logic [7:0] command; - logic [15:0] status; - logic [15:0] data_input; - logic [15:0] data_output; - logic bm_request; - logic [15:0] bm_control; - logic [15:0] bm_status; + logic [15:0] data; + logic [7:0] cmd; + logic cmd_pending; + logic cmd_interrupt; + logic bm_start_pending; + logic bm_stop_pending; + logic bm_transfer_mode; + logic bm_transfer_blocks; + logic bm_pending; + logic bm_interrupt; + logic bm_interrupt_ack; + logic [7:0] sector_num; + logic [7:0] sector_size; + logic [7:0] sector_size_full; + logic [7:0] sectors_in_block; - modport n64 ( + + // CPU controlled regs + + logic hard_reset_clear; + logic [15:0] cmd_data; + logic cmd_ready; + logic bm_start_clear; + logic bm_stop_clear; + logic bm_transfer_c2; + logic bm_transfer_data; + logic bm_micro_error; + logic bm_clear; + logic bm_ready; + logic disk_inserted; + logic disk_changed; + logic index_lock; + logic [12:0] head_track; + logic [15:0] drive_id; + + + always_comb begin + dd_interrupt = cmd_interrupt || bm_interrupt; + end + + + modport dd ( output hard_reset, - output cmd_request, - input cmd_ack, - output command, - input status, - output data_input, - input data_output, - output bm_request, - output bm_control, - input bm_status + output data, + output cmd, + output cmd_pending, + output cmd_interrupt, + output bm_start_pending, + output bm_stop_pending, + output bm_transfer_mode, + output bm_transfer_blocks, + output bm_pending, + output bm_interrupt, + output bm_interrupt_ack, + output sector_num, + output sector_size, + output sector_size_full, + output sectors_in_block, + + input hard_reset_clear, + input cmd_data, + input cmd_ready, + input bm_start_clear, + input bm_stop_clear, + input bm_transfer_c2, + input bm_transfer_data, + input bm_micro_error, + input bm_clear, + input bm_ready, + input disk_inserted, + input disk_changed, + input index_lock, + input head_track, + input drive_id, + + output .sector_address(n64_sector_address), + output .sector_address_valid(n64_sector_address_valid), + output .sector_write(n64_sector_write), + output .sector_wdata(n64_sector_wdata), + input sector_rdata ); modport cpu ( input hard_reset, - input cmd_request, - output cmd_ack, - input command, - output status, - input data_input, - output data_output, - input bm_request, - input bm_control, - output bm_status + input data, + input cmd, + input cmd_pending, + input bm_start_pending, + input bm_stop_pending, + input bm_transfer_mode, + input bm_transfer_blocks, + input bm_pending, + input bm_interrupt_ack, + input sector_num, + input sector_size, + input sector_size_full, + input sectors_in_block, + + output hard_reset_clear, + output cmd_data, + output cmd_ready, + output bm_start_clear, + output bm_stop_clear, + output bm_transfer_c2, + output bm_transfer_data, + output bm_micro_error, + output bm_ready, + output bm_clear, + output disk_inserted, + output disk_changed, + output index_lock, + output head_track, + output drive_id, + + output .sector_address(cpu_sector_address), + output .sector_address_valid(cpu_sector_address_valid), + output .sector_write(cpu_sector_write), + output .sector_wdata(cpu_sector_wdata), + input sector_rdata + ); + + modport sector_buffer ( + input n64_sector_address, + input n64_sector_address_valid, + input n64_sector_write, + input n64_sector_wdata, + + input cpu_sector_address, + input cpu_sector_address_valid, + input cpu_sector_write, + input cpu_sector_wdata, + + output sector_rdata ); endinterface @@ -42,9 +160,33 @@ endinterface module n64_dd ( if_system.sys sys, - if_n64_bus bus + if_n64_bus bus, + if_dd.dd dd ); + const bit [31:0] M_BASE = 32'h0500_0000; + const bit [31:0] M_C2_BUFFER = M_BASE + 11'h000; + const bit [31:0] M_SECTOR_BUFFER = M_BASE + 11'h400; + + typedef enum bit [10:0] { + R_DATA = 11'h500, + R_CMD_SR = 11'h508, + R_TRK_CUR = 11'h50C, + R_BM_SCR = 11'h510, + R_RESET = 11'h520, + R_SEC_SIZ = 11'h528, + R_SEC_INFO = 11'h530, + R_ID = 11'h540 + } e_reg_id; + + typedef enum bit [3:0] { + BM_CONTROL_START_BUFFER_MANAGER = 4'd15, + BM_CONTROL_BUFFER_MANAGER_MODE = 4'd14, + BM_CONTROL_BUFFER_MANAGER_RESET = 4'd12, + BM_CONTROL_BLOCK_TRANSFER = 4'd9, + BM_CONTROL_MECHANIC_INTERRUPT_RESET = 4'd8 + } e_bm_control_id; + typedef enum bit [0:0] { S_IDLE, S_WAIT @@ -53,23 +195,155 @@ module n64_dd ( e_state state; always_comb begin - bus.rdata = 16'h0000; + dd.sector_address = bus.address[7:1]; + dd.sector_address_valid = bus.request && bus.address[11:8] == M_SECTOR_BUFFER[11:8]; + dd.sector_write = bus.write && dd.sector_address_valid; + dd.sector_wdata = bus.wdata; + end + + always_comb begin + bus.rdata = 16'd0; if (bus.ack) begin - bus.rdata = !bus.address[1] ? 16'h0040 : 16'h0000; + if (bus.address[10:8] == M_SECTOR_BUFFER[10:8]) begin + if (bus.address[1]) begin + bus.rdata = dd.sector_rdata[15:0]; + end else begin + bus.rdata = dd.sector_rdata[31:16]; + end + end else begin + case (bus.address[10:0]) + R_DATA: bus.rdata = dd.data; + R_CMD_SR: bus.rdata = { + 1'b0, + dd.bm_transfer_data, + 1'b0, + dd.bm_transfer_c2, + 1'b0, + dd.bm_interrupt, + dd.cmd_interrupt, + dd.disk_inserted, + dd.cmd_pending, + dd.hard_reset, + 1'b0, + 1'b0, + 1'b0, + 1'b0, + 1'b0, + dd.disk_changed + }; + R_TRK_CUR: bus.rdata = {1'd0, {2{dd.index_lock}}, dd.head_track}; + R_BM_SCR: bus.rdata = {6'd0, dd.bm_micro_error, 9'd0}; + R_ID: bus.rdata = {dd.drive_id}; + default: bus.rdata = 16'd0; + endcase + end end end always_ff @(posedge sys.clk) begin bus.ack <= 1'b0; + dd.bm_interrupt_ack <= 1'b0; - if (sys.reset) begin + if (dd.hard_reset_clear) begin + dd.hard_reset <= 1'b0; + end + if (dd.cmd_ready) begin + dd.data <= dd.cmd_data; + dd.cmd_pending <= 1'b0; + dd.cmd_interrupt <= 1'b1; + end + if (dd.bm_start_clear) begin + dd.bm_start_pending <= 1'b0; + end + if (dd.bm_stop_clear) begin + dd.bm_stop_pending <= 1'b0; + end + if (dd.bm_clear) begin + dd.bm_pending <= 1'b0; + end + if (dd.bm_ready) begin + dd.bm_interrupt <= 1'b1; + end + if (bus.real_address == (M_C2_BUFFER + ({dd.sector_size[7:1], 1'b0} * 3'd4)) && bus.read_op) begin + dd.bm_pending <= 1'b1; + end + if (bus.real_address == (M_SECTOR_BUFFER + {dd.sector_size[7:1], 1'b0}) && (bus.read_op || bus.write_op)) begin + dd.bm_pending <= 1'b1; + end + if (bus.real_address == (M_BASE + R_CMD_SR) && bus.read_op) begin + dd.bm_interrupt <= 1'b0; + dd.bm_interrupt_ack <= 1'b1; + end + + if (sys.reset || sys.n64_hard_reset) begin + dd.hard_reset <= 1'b1; + dd.cmd_pending <= 1'b0; + dd.cmd_interrupt <= 1'b0; + dd.bm_start_pending <= 1'b0; + dd.bm_stop_pending <= 1'b0; + dd.bm_pending <= 1'b0; + dd.bm_interrupt <= 1'b0; state <= S_IDLE; - end else begin + end else begin case (state) S_IDLE: begin if (bus.request) begin state <= S_WAIT; bus.ack <= 1'b1; + if (bus.write) begin + case (bus.address[10:0]) + R_DATA: begin + dd.data <= bus.wdata; + end + + R_CMD_SR: begin + dd.cmd <= bus.wdata[7:0]; + dd.cmd_pending <= 1'b1; + end + + R_BM_SCR: begin + dd.sector_num <= bus.wdata[7:0]; + if (bus.wdata[BM_CONTROL_START_BUFFER_MANAGER]) begin + dd.bm_start_pending <= 1'b1; + dd.bm_stop_pending <= 1'b0; + dd.bm_transfer_mode <= bus.wdata[BM_CONTROL_BUFFER_MANAGER_MODE]; + dd.bm_transfer_blocks <= bus.wdata[BM_CONTROL_BLOCK_TRANSFER]; + end + if (bus.wdata[BM_CONTROL_BUFFER_MANAGER_RESET]) begin + dd.bm_start_pending <= 1'b0; + dd.bm_stop_pending <= 1'b1; + dd.bm_transfer_mode <= 1'b0; + dd.bm_transfer_blocks <= 1'b0; + dd.bm_pending <= 1'b0; + dd.bm_interrupt <= 1'b0; + end + if (bus.wdata[BM_CONTROL_MECHANIC_INTERRUPT_RESET]) begin + dd.cmd_interrupt <= 1'b0; + end + end + + R_RESET: begin + if (bus.wdata == 16'hAAAA) begin + dd.hard_reset <= 1'b1; + dd.cmd_pending <= 1'b0; + dd.cmd_interrupt <= 1'b0; + dd.bm_start_pending <= 1'b0; + dd.bm_stop_pending <= 1'b0; + dd.bm_pending <= 1'b0; + dd.bm_interrupt <= 1'b0; + end + end + + R_SEC_SIZ: begin + dd.sector_size <= bus.wdata[7:0]; + end + + R_SEC_INFO: begin + dd.sectors_in_block <= bus.wdata[15:8]; + dd.sector_size_full <= bus.wdata[7:0]; + end + endcase + end end end @@ -81,3 +355,50 @@ module n64_dd ( end endmodule + + +module n64_dd_sector_buffer ( + if_system.sys sys, + if_dd.sector_buffer dd +); + + logic [5:0] sector_address; + logic [31:0] sector_buffer [0:63]; + logic [15:0] sector_high_buffer; + logic sector_write; + logic [31:0] sector_wdata; + + always_comb begin + sector_address = 6'd0; + sector_write = 1'b0; + sector_wdata = 32'd0; + + if (dd.n64_sector_address_valid) begin + sector_address = dd.n64_sector_address[6:1]; + end else if (dd.cpu_sector_address_valid) begin + sector_address = dd.cpu_sector_address; + end + + if (dd.n64_sector_write && dd.n64_sector_address[0]) begin + sector_write = 1'b1; + sector_wdata = {sector_high_buffer, dd.n64_sector_wdata}; + end else if (dd.cpu_sector_write) begin + sector_write = 1'b1; + sector_wdata = dd.cpu_sector_wdata; + end + end + + always_ff @(posedge sys.clk) begin + if (dd.n64_sector_write && !dd.n64_sector_address[0]) begin + sector_high_buffer <= dd.n64_sector_wdata; + end + end + + always_ff @(posedge sys.clk) begin + dd.sector_rdata <= sector_buffer[sector_address]; + if (sector_write) begin + sector_buffer[sector_address] <= sector_wdata; + end + end + +endmodule diff --git a/fw/rtl/n64/n64_pi.sv b/fw/rtl/n64/n64_pi.sv index 3ae1ad7..8d012c8 100644 --- a/fw/rtl/n64/n64_pi.sv +++ b/fw/rtl/n64/n64_pi.sv @@ -190,6 +190,23 @@ module n64_pi ( end end + always_comb begin + bus.read_op = read_op; + bus.write_op = write_op; + end + + always_ff @(posedge sys.clk) begin + if (aleh_op) begin + bus.real_address[31:16] <= n64_pi_ad_input; + end + if (alel_op) begin + bus.real_address[15:0] <= {n64_pi_ad_input[15:1], 1'b0}; + end + if (read_op || write_op) begin + bus.real_address <= bus.real_address + 2'd2; + end + end + // Address decoding @@ -253,6 +270,11 @@ module n64_pi ( end end if (alel_op) begin + if (next_id == sc64::ID_N64_DD) begin + if (|n64_pi_ad_input[15:11]) begin + n64_pi_address_valid <= 1'b0; + end + end if (sram_selected) begin if (n64_pi_ad_input[15]) begin n64_pi_address_valid <= 1'b0; diff --git a/fw/rtl/n64/n64_soc.sv b/fw/rtl/n64/n64_soc.sv index 4e6c5a3..2f1a1a4 100644 --- a/fw/rtl/n64/n64_soc.sv +++ b/fw/rtl/n64/n64_soc.sv @@ -6,6 +6,7 @@ module n64_soc ( if_flashram.flashram flashram, if_si.si si, if_flash.memory flash, + if_dd dd, input n64_pi_alel, input n64_pi_aleh, @@ -77,7 +78,13 @@ module n64_soc ( n64_dd n64_dd_inst ( .sys(sys), - .bus(bus.at[sc64::ID_N64_DD].device) + .bus(bus.at[sc64::ID_N64_DD].device), + .dd(dd) + ); + + n64_dd_sector_buffer n64_dd_sector_buffer_inst ( + .sys(sys), + .dd(dd) ); n64_cfg n64_cfg_inst ( diff --git a/fw/rtl/system/sc64.sv b/fw/rtl/system/sc64.sv index 92aa772..9facdb1 100644 --- a/fw/rtl/system/sc64.sv +++ b/fw/rtl/system/sc64.sv @@ -21,6 +21,7 @@ package sc64; ID_CPU_SDRAM, ID_CPU_FLASHRAM, ID_CPU_SI, + ID_CPU_DD, __ID_CPU_END } e_cpu_id; diff --git a/sw/n64/src/boot.c b/sw/n64/src/boot.c index c86efc8..756aa93 100644 --- a/sw/n64/src/boot.c +++ b/sw/n64/src/boot.c @@ -14,13 +14,13 @@ typedef struct { } ipl3_crc32_t; static const ipl3_crc32_t ipl3_crc32[] = { - { .crc32 = 0x587BD543, .seed = 0xAC, .version = 0 }, // CIC5101 - { .crc32 = 0x6170A4A1, .seed = 0x3F, .version = 1 }, // CIC6101 - { .crc32 = 0x009E9EA3, .seed = 0x3F, .version = 1 }, // CIC7102 - { .crc32 = 0x90BB6CB5, .seed = 0x3F, .version = 0 }, // CICx102 - { .crc32 = 0x0B050EE0, .seed = 0x78, .version = 0 }, // CICx103 - { .crc32 = 0x98BC2C86, .seed = 0x91, .version = 0 }, // CICx105 - { .crc32 = 0xACC8580A, .seed = 0x85, .version = 0 }, // CICx106 + { .crc32 = 0x587BD543, .seed = 0xAC, .version = 0 }, // 5101 + { .crc32 = 0x6170A4A1, .seed = 0x3F, .version = 1 }, // 6101 + { .crc32 = 0x009E9EA3, .seed = 0x3F, .version = 1 }, // 7102 + { .crc32 = 0x90BB6CB5, .seed = 0x3F, .version = 0 }, // x102 + { .crc32 = 0x0B050EE0, .seed = 0x78, .version = 0 }, // x103 + { .crc32 = 0x98BC2C86, .seed = 0x91, .version = 0 }, // x105 + { .crc32 = 0xACC8580A, .seed = 0x85, .version = 0 }, // x106 { .crc32 = 0x10C68B18, .seed = 0xDD, .version = 0 }, // NDXJ0 { .crc32 = 0xBC605D0A, .seed = 0xDD, .version = 0 }, // NDDJ0 { .crc32 = 0x502C4466, .seed = 0xDD, .version = 0 }, // NDDJ1 @@ -43,26 +43,13 @@ bool boot_get_tv_type (boot_info_t *info) { char region = ((pi_io_read(&base[15]) >> 8) & 0xFF); switch (region) { - case 'D': - case 'F': - case 'H': - case 'I': case 'P': - case 'S': - case 'W': - case 'X': - case 'Y': + case 'U': info->tv_type = BOOT_TV_TYPE_PAL; break; - case '7': - case 'A': - case 'C': case 'E': case 'J': - case 'K': - case 'N': - case 'U': info->tv_type = BOOT_TV_TYPE_NTSC; break; diff --git a/sw/n64/src/main.c b/sw/n64/src/main.c index 520572f..cc429a9 100644 --- a/sw/n64/src/main.c +++ b/sw/n64/src/main.c @@ -65,7 +65,7 @@ void main (void) { } } - LOG_I("Booting IPL3\r\n\r\n"); + LOG_I("Booting IPL3\033[0m\r\n\r\n"); boot(&boot_info); } diff --git a/sw/pc/sc64.py b/sw/pc/sc64.py index 6360fc5..93eece2 100644 --- a/sw/pc/sc64.py +++ b/sw/pc/sc64.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +from io import TextIOWrapper from serial import Serial, SerialException from serial.tools import list_ports import argparse @@ -7,6 +8,7 @@ import filecmp import os import progressbar import re +import struct import sys import time @@ -18,9 +20,6 @@ class SC64Exception(Exception): 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 @@ -32,6 +31,8 @@ class SC64: __CFG_ID_FLASH_READ = 11 __CFG_ID_FLASH_PROGRAM = 12 __CFG_ID_RECONFIGURE = 13 + __CFG_ID_DD_SETTING = 14 + __CFG_ID_DD_THB_TABLE_OFFSET = 15 __SC64_VERSION_V2 = 0x53437632 @@ -46,7 +47,14 @@ class SC64: __DEBUG_ID_FSD_READ = 0xF1 __DEBUG_ID_FSD_WRITE = 0xF2 __DEBUG_ID_FSD_SECTOR = 0xF3 + __DEBUG_ID_DD_BLOCK = 0xF5 + __DD_SETTING_DISK_EJECTED = 0 + __DD_SETTING_DISK_INSERTED = 1 + __DD_SETTING_DISK_CHANGED = 2 + __DD_SETTING_DRIVE_RETAIL = 3 + __DD_SETTING_DRIVE_DEVELOPMENT = 4 + __DD_SETTING_SET_BLOCK_READY = 5 def __init__(self) -> None: self.__serial = None @@ -54,6 +62,8 @@ class SC64: self.__progress_value = None self.__progress_finish = None self.__fsd_file = None + self.__disk_file = None + self.__disk_lba_table = [] self.__find_sc64() @@ -87,8 +97,11 @@ class SC64: return re.sub(b"\x1B", b"\x1B\x1B", data) - def __reset_link(self) -> None: + def reset_link(self) -> None: self.__serial.write(b"\x1BR") + while (self.__serial.in_waiting): + self.__serial.read_all() + time.sleep(0.1) def __read(self, bytes: int) -> bytes: @@ -141,10 +154,7 @@ class 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.reset_link() self.__probe_device() except (SerialException, SC64Exception): if (self.__serial): @@ -360,6 +370,174 @@ class SC64: self.__write_file_to_sdram(file, dd_ipl_offset, min_length=self.__DDIPL_ROM_LENGTH) + def set_dd_disk_state(self, state: int) -> None: + if (state == "ejected"): + self.__change_config(self.__CFG_ID_DD_SETTING, self.__DD_SETTING_DISK_EJECTED) + elif (state == "inserted"): + self.__change_config(self.__CFG_ID_DD_SETTING, self.__DD_SETTING_DISK_INSERTED) + elif (state == "changed"): + self.__change_config(self.__CFG_ID_DD_SETTING, self.__DD_SETTING_DISK_CHANGED) + else: + raise SC64Exception("DD disk state outside of supported values") + + + def __dd_create_configuration(self, handle: TextIOWrapper) -> tuple[str, int, list[tuple[int, int]]]: + DISK_HEADS = 2 + DISK_TRACKS = 1175 + DISK_BLOCKS_PER_TRACK = 2 + DISK_SECTORS_PER_BLOCK = 85 + DISK_BAD_TRACKS_PER_ZONE = 12 + DISK_SYSTEM_SECTOR_SIZE = 232 + + DISK_ZONES = [ + (0, 232, 158, 0), + (0, 216, 158, 158), + (0, 208, 149, 316), + (0, 192, 149, 465), + (0, 176, 149, 614), + (0, 160, 149, 763), + (0, 144, 149, 912), + (0, 128, 114, 1061), + (1, 216, 158, 157), + (1, 208, 158, 315), + (1, 192, 149, 464), + (1, 176, 149, 613), + (1, 160, 149, 762), + (1, 144, 149, 911), + (1, 128, 149, 1060), + (1, 112, 114, 1174), + ] + + DISK_VZONE_TO_PZONE = [ + [0, 1, 2, 9, 8, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10], + [0, 1, 2, 3, 10, 9, 8, 4, 5, 6, 7, 15, 14, 13, 12, 11], + [0, 1, 2, 3, 4, 11, 10, 9, 8, 5, 6, 7, 15, 14, 13, 12], + [0, 1, 2, 3, 4, 5, 12, 11, 10, 9, 8, 6, 7, 15, 14, 13], + [0, 1, 2, 3, 4, 5, 6, 13, 12, 11, 10, 9, 8, 7, 15, 14], + [0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8, 15], + [0, 1, 2, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10, 9, 8], + ] + + DISK_DRIVE_TYPES = [( + "development", + 192, + [11, 10, 3, 2], + [0, 1, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23], + ), ( + "retail", + 232, + [9, 8, 1, 0], + [2, 3, 10, 11, 12, 16, 17, 18, 19, 20, 21, 22, 23], + )] + + def __check_system_block(lba: int, sector_size: int, check_disk_type: bool) -> tuple[bool, bytes]: + handle.seek(lba * DISK_SYSTEM_SECTOR_SIZE * DISK_SECTORS_PER_BLOCK) + system_block_data = handle.read(sector_size * DISK_SECTORS_PER_BLOCK) + system_data = system_block_data[:sector_size] + for sector in range(1, DISK_SECTORS_PER_BLOCK): + sector_data = system_block_data[(sector * sector_size):][:sector_size] + if (system_data != sector_data): + return (False, None) + if (check_disk_type): + if (system_data[4] != 0x10): + return (False, None) + if ((system_data[5] & 0xF0) != 0x10): + return (False, None) + return (True, system_data) + + disk_drive_type = None + disk_system_data = None + disk_id_data = None + disk_bad_lbas = [] + + drive_index = 0 + while (disk_system_data == None) and (drive_index < len(DISK_DRIVE_TYPES)): + (drive_type, system_sector_size, system_data_lbas, bad_lbas) = DISK_DRIVE_TYPES[drive_index] + disk_bad_lbas.clear() + disk_bad_lbas.extend(bad_lbas) + for system_lba in system_data_lbas: + (valid, system_data) = __check_system_block(system_lba, system_sector_size, check_disk_type=True) + if (valid): + disk_drive_type = drive_type + disk_system_data = system_data + else: + disk_bad_lbas.append(system_lba) + drive_index += 1 + + for id_lba in [15, 14]: + (valid, id_data) = __check_system_block(id_lba, DISK_SYSTEM_SECTOR_SIZE, check_disk_type=False) + if (valid): + disk_id_data = id_data + else: + disk_bad_lbas.append(id_lba) + + if not (disk_system_data and disk_id_data): + raise SC64Exception("Provided 64DD disk file is not valid") + + disk_zone_bad_tracks = [] + + for zone in range(len(DISK_ZONES)): + zone_bad_tracks = [] + start = 0 if zone == 0 else system_data[0x07 + zone] + stop = system_data[0x07 + zone + 1] + for offset in range(start, stop): + zone_bad_tracks.append(system_data[0x20 + offset]) + for ignored_track in range(DISK_BAD_TRACKS_PER_ZONE - len(zone_bad_tracks)): + zone_bad_tracks.append(DISK_ZONES[zone][2] - ignored_track - 1) + disk_zone_bad_tracks.append(zone_bad_tracks) + + thb_lba_table = [(0xFFFFFFFF, -1)] * (DISK_HEADS * DISK_TRACKS * DISK_BLOCKS_PER_TRACK) + + disk_type = disk_system_data[5] & 0x0F + + current_lba = 0 + starting_block = 0 + disk_file_offset = 0 + + for zone in DISK_VZONE_TO_PZONE[disk_type]: + (head, sector_size, tracks, track) = DISK_ZONES[zone] + + for zone_track in range(tracks): + current_zone_track = ((tracks - 1) - zone_track) if head else zone_track + + if (current_zone_track in disk_zone_bad_tracks[zone]): + track += (-1) if head else 1 + continue + + for block in range(DISK_BLOCKS_PER_TRACK): + if (current_lba not in disk_bad_lbas): + index = (track << 2) | (head << 1) | (starting_block ^ block) + thb_lba_table[index] = (disk_file_offset, current_lba) + disk_file_offset += sector_size * DISK_SECTORS_PER_BLOCK + current_lba += 1 + + track += (-1) if head else 1 + starting_block ^= 1 + + return (disk_drive_type, thb_lba_table) + + + def set_dd_configuration_for_disk(self, file: str = None) -> None: + if (file): + with open(file, "rb+") as handle: + (disk_drive_type, thb_lba_table) = self.__dd_create_configuration(handle) + thb_table_offset = self.__query_config(self.__CFG_ID_DD_THB_TABLE_OFFSET) + data = bytearray() + self.__disk_lba_table = [0xFFFFFFFF] * len(thb_lba_table) + for (offset, lba) in thb_lba_table: + data += struct.pack(">I", offset) + self.__disk_lba_table[lba] = offset + self.__write_cmd("W", thb_table_offset, len(data)) + self.__write(data) + self.__read_cmd_status("W") + if (disk_drive_type == "retail"): + self.__change_config(self.__CFG_ID_DD_SETTING, self.__DD_SETTING_DRIVE_RETAIL) + elif (disk_drive_type == "development"): + self.__change_config(self.__CFG_ID_DD_SETTING, self.__DD_SETTING_DRIVE_DEVELOPMENT) + else: + raise SC64Exception("No DD disk file provided for disk info creation") + + def __debug_process_fsd_set_sector(self, data: bytes) -> None: sector = int.from_bytes(data[0:4], byteorder='big') if (self.__fsd_file): @@ -379,14 +557,36 @@ class SC64: self.__fsd_file.write(data) - def debug_loop(self, file: str = None) -> None: + def __debug_process_dd_block(self, data: bytes) -> None: + transfer_mode = int.from_bytes(data[0:4], byteorder='big') + sdram_offset = int.from_bytes(data[4:8], byteorder='big') + disk_file_offset = int.from_bytes(data[8:12], byteorder='big') + block_length = int.from_bytes(data[12:16], byteorder='big') + + print(f"64DD Block {'R' if transfer_mode else 'W'} - LBA: {self.__disk_lba_table.index(disk_file_offset):4}, Offset: 0x{disk_file_offset:08X}") + + if (self.__disk_file): + self.__disk_file.seek(disk_file_offset) + if (transfer_mode): + self.__write_cmd("W", sdram_offset, block_length) + self.__write(self.__disk_file.read(block_length)) + self.__read_cmd_status("W") + else: + self.__write_cmd("R", sdram_offset, block_length) + self.__disk_file.write(self.__read_long(block_length)) + self.__read_cmd_status("R") + + self.__change_config(self.__CFG_ID_DD_SETTING, self.__DD_SETTING_SET_BLOCK_READY) + + + def debug_loop(self, fsd_file: str = None, disk_file: str = None) -> None: print("\r\n\033[34m --- Debug server started --- \033[0m\r\n") - self.__serial.timeout = 0.01 - self.__serial.write_timeout = 1 + if (fsd_file): + self.__fsd_file = open(fsd_file, "rb+") - if (file): - self.__fsd_file = open(file, "rb+") + if (disk_file): + self.__disk_file = open(disk_file, "rb+") start_indicator = bytearray() dropped_bytes = 0 @@ -406,10 +606,13 @@ class SC64: header = self.__read_long(4) id = int(header[0]) length = int.from_bytes(header[1:4], byteorder="big") + data = self.__read_long(length) + self.__read_long(self.__align(length, 4) - length) + end_indicator = self.__read_long(4) - if (length > 0): - data = self.__read_long(length) - + if (end_indicator != b"CMPH"): + print(f"\033[35mGot unknown end indicator: {end_indicator.decode(encoding='ascii', errors='backslashreplace')}\033[0m", file=sys.stderr) + else: if (id == self.__DEBUG_ID_TEXT): print(data.decode(encoding="ascii", errors="backslashreplace"), end="") elif (id == self.__DEBUG_ID_FSD_READ): @@ -418,14 +621,11 @@ class SC64: self.__debug_process_fsd_write(data) elif (id == self.__DEBUG_ID_FSD_SECTOR): self.__debug_process_fsd_set_sector(data) + elif (id == self.__DEBUG_ID_DD_BLOCK): + self.__debug_process_dd_block(data) else: print(f"\033[35mGot unknown id: {id}, length: {length}\033[0m", file=sys.stderr) - self.__read_long(self.__align(length, 4) - length) - end_indicator = self.__read_long(4) - if (end_indicator != b"CMPH"): - print(f"\033[35mGot unknown end indicator: {end_indicator.decode(encoding='ascii', errors='backslashreplace')}\033[0m", file=sys.stderr) - class SC64ProgressBar: @@ -499,6 +699,7 @@ if __name__ == "__main__": parser.add_argument("-i", metavar="ddipl_path", default=None, required=False, help="path to DDIPL file") parser.add_argument("-q", default=None, action="store_true", required=False, help="start debug server") parser.add_argument("-f", metavar="sd_path", default=None, required=False, help="path to disk or file for fake SD card emulation") + parser.add_argument("-k", metavar="disk_path", default=None, required=False, help="path to 64DD disk file") parser.add_argument("rom", metavar="rom_path", default=None, help="path to ROM file", nargs="?") if (len(sys.argv) <= 1): @@ -523,6 +724,7 @@ if __name__ == "__main__": rom_file = args.rom debug_server = args.q sd_file = args.f + disk_file = args.k firmware_backup_file = "sc64firmware.bin.bak" @@ -582,7 +784,15 @@ if __name__ == "__main__": sc64.upload_save(save_file) if (debug_server): - sc64.debug_loop(sd_file) + if (sd_file): + print(f"Using fake SD emulation file [{sd_file}]") + if (disk_file): + print(f"Using 64DD disk image file [{disk_file}]") + sc64.set_dd_configuration_for_disk(disk_file) + if (disk_file): + print(f"Setting 64DD disk state to [Changed]") + sc64.set_dd_disk_state("changed" if disk_file else "ejected") + sc64.debug_loop(sd_file, disk_file) except SC64Exception as e: print(f"Error: {e}") @@ -590,4 +800,8 @@ if __name__ == "__main__": except KeyboardInterrupt: pass finally: + sc64.reset_link() + if (disk_file): + print(f"Setting 64DD disk state to [Ejected]") + sc64.set_dd_disk_state("ejected") sys.stdout.write("\033[0m") diff --git a/sw/riscv/Makefile b/sw/riscv/Makefile index 1253a1b..566e32b 100644 --- a/sw/riscv/Makefile +++ b/sw/riscv/Makefile @@ -12,7 +12,7 @@ LDFLAGS = -nostartfiles -Wl,--gc-sections SRC_DIR = src BUILD_DIR = build -SRC_FILES = startup.S process.c usb.c cfg.c dma.c joybus.c rtc.c i2c.c flashram.c uart.c flash.c +SRC_FILES = startup.S process.c usb.c cfg.c dma.c joybus.c rtc.c i2c.c flashram.c uart.c flash.c dd.c SRCS = $(addprefix $(SRC_DIR)/, $(SRC_FILES)) OBJS = $(addprefix $(BUILD_DIR)/, $(notdir $(patsubst %,%.o,$(SRCS)))) diff --git a/sw/riscv/src/cfg.c b/sw/riscv/src/cfg.c index 8933bb8..1232c05 100644 --- a/sw/riscv/src/cfg.c +++ b/sw/riscv/src/cfg.c @@ -1,4 +1,5 @@ #include "cfg.h" +#include "dd.h" #include "flash.h" #include "joybus.h" #include "usb.h" @@ -31,6 +32,8 @@ enum cfg_id { CFG_ID_FLASH_READ, CFG_ID_FLASH_PROGRAM, CFG_ID_RECONFIGURE, + CFG_ID_DD_SETTING, + CFG_ID_DD_THB_TABLE_OFFSET, }; enum save_type { @@ -50,6 +53,15 @@ enum boot_mode { BOOT_MODE_DIRECT = 3, }; +enum dd_setting { + DD_SETTING_DISK_EJECTED = 0, + DD_SETTING_DISK_INSERTED = 1, + DD_SETTING_DISK_CHANGED = 2, + DD_SETTING_DRIVE_RETAIL = 3, + DD_SETTING_DRIVE_DEVELOPMENT = 4, + DD_SETTING_SET_BLOCK_READY = 5, +}; + struct process { enum save_type save_type; @@ -112,6 +124,29 @@ static void set_save_type (enum save_type save_type) { CFG->SAVE_OFFSET = save_offset; } +static void set_dd_setting (enum dd_setting setting) { + switch (setting) { + case DD_SETTING_DISK_EJECTED: + dd_set_disk_state(DD_DISK_EJECTED); + break; + case DD_SETTING_DISK_INSERTED: + dd_set_disk_state(DD_DISK_INSERTED); + break; + case DD_SETTING_DISK_CHANGED: + dd_set_disk_state(DD_DISK_CHANGED); + break; + case DD_SETTING_DRIVE_RETAIL: + dd_set_drive_type_development(false); + break; + case DD_SETTING_DRIVE_DEVELOPMENT: + dd_set_drive_type_development(true); + break; + case DD_SETTING_SET_BLOCK_READY: + dd_set_block_ready(true); + break; + } +} + uint32_t cfg_get_version (void) { return CFG->VERSION; @@ -159,11 +194,14 @@ void cfg_update (uint32_t *args) { case CFG_ID_RECONFIGURE: if (args[1] == CFG->RECONFIGURE) { CFG->RECONFIGURE = args[1]; - __asm__ volatile ( + asm volatile ( "ebreak \n" ); } break; + case CFG_ID_DD_SETTING: + set_dd_setting(args[1]); + break; } } @@ -205,6 +243,9 @@ void cfg_query (uint32_t *args) { case CFG_ID_RECONFIGURE: args[1] = CFG->RECONFIGURE; break; + case CFG_ID_DD_THB_TABLE_OFFSET: + args[1] = dd_get_thb_table_offset(); + break; } } diff --git a/sw/riscv/src/dd.c b/sw/riscv/src/dd.c new file mode 100644 index 0000000..8ea156c --- /dev/null +++ b/sw/riscv/src/dd.c @@ -0,0 +1,440 @@ +#include "dd.h" +#include "rtc.h" +#include "usb.h" + + +#define DD_USER_SECTORS_PER_BLOCK (85) + +#define DD_BUFFERS_OFFSET (SDRAM_BASE + 0x03BD0000UL) +#define DD_THB_TABLE_OFFSET (DD_BUFFERS_OFFSET + 0x0000) +#define DD_USB_BUFFER_OFFSET (DD_BUFFERS_OFFSET + 0x5000) +#define DD_BLOCK_BUFFER_OFFSET (DD_BUFFERS_OFFSET + 0x6000) + +#define USB_DEBUG_ID_DD_BLOCK (0xF5) + +#define DD_DRIVE_ID_RETAIL (0x0003) +#define DD_DRIVE_ID_DEVELOPMENT (0x0004) +#define DD_VERSION_RETAIL (0x0114) + +#define DD_POWER_UP_DELAY_TICKS (200000000UL) // 2 s +#define DD_TRACK_SEEK_TIME_TICKS (10000) // 0.1 ms + + +typedef enum { + DD_CMD_SEEK_READ = 0x01, + DD_CMD_SEEK_WRITE = 0x02, + DD_CMD_CLEAR_DISK_CHANGE = 0x08, + DD_CMD_CLEAR_RESET_STATE = 0x09, + DD_CMD_READ_VERSION = 0x0A, + DD_CMD_SET_DISK_TYPE = 0x0B, + DD_CMD_REQUEST_STATUS = 0x0C, + DD_CMD_SET_RTC_YEAR_MONTH = 0x0F, + DD_CMD_SET_RTC_DAY_HOUR = 0x10, + DD_CMD_SET_RTC_MINUTE_SECOND = 0x11, + DD_CMD_GET_RTC_YEAR_MONTH = 0x12, + DD_CMD_GET_RTC_DAY_HOUR = 0x13, + DD_CMD_GET_RTC_MINUTE_SECOND = 0x14, + DD_CMD_READ_PROGRAM_VERSION = 0x1B, +} dd_cmd_t; + + +enum state { + STATE_IDLE, + STATE_START, + STATE_BLOCK_READ, + STATE_BLOCK_READ_WAIT, + STATE_SECTOR_READ, + STATE_SECTOR_WRITE, + STATE_BLOCK_WRITE, + STATE_BLOCK_WRITE_WAIT, + STATE_NEXT_BLOCK, + STATE_STOP, +}; + +struct process { + enum state state; + uint32_t track_seek_time; + uint32_t next_seek_time; + bool power_up_delay; + bool deffered_cmd_ready; + bool bm_running; + bool transfer_mode; + bool full_track_transfer; + bool starting_block; + uint8_t current_sector; + bool is_dev_disk; + rtc_time_t time; + io32_t *thb_table; + io32_t *usb_buffer; + io32_t *block_buffer; + bool block_ready; +}; + +static struct process p; + + +static uint16_t dd_track_head_block (void) { + uint32_t head_track = DD->HEAD_TRACK; + uint16_t track = ((head_track & DD_TRACK_MASK) << 2); + uint16_t head = (((head_track & DD_HEAD_MASK) ? 1 : 0) << 1); + uint16_t block = (p.starting_block ? 1 : 0); + + return (track | head | block); +} + +static bool dd_block_valid (void) { + return (p.thb_table[dd_track_head_block()] != 0xFFFFFFFF); +} + +static bool dd_block_request (void) { + if (!usb_debug_tx_ready()) { + return false; + } + + if (!(DD->SCR & DD_SCR_DISK_INSERTED)) { + return true; + } + + const char *dma = "DMA@"; + const char *cmp = "CMPH"; + + io32_t offset = p.thb_table[dd_track_head_block()]; + uint32_t length = ((DD->SECTOR_SIZE + 1) * DD_USER_SECTORS_PER_BLOCK); + + io32_t *dst = p.usb_buffer; + + *dst++ = *((uint32_t *) (dma)); + *dst++ = swap32((USB_DEBUG_ID_DD_BLOCK << 24) | 16); + *dst++ = swap32(p.transfer_mode); + *dst++ = swap32((uint32_t) (p.block_buffer)); + *dst++ = offset; + *dst++ = swap32(length); + *dst++ = *((uint32_t *) (cmp)); + + usb_debug_tx_data((uint32_t) (p.usb_buffer), 28); + + p.block_ready = false; + + return true; +} + +static bool dd_block_ready (void) { + return p.block_ready || (!(DD->SCR & DD_SCR_DISK_INSERTED)); +} + +static void dd_sector_read (void) { + io32_t *src = p.block_buffer; + io32_t *dst = DD->SECTOR_BUFFER; + + uint8_t sector_size = ((DD->SECTOR_SIZE + 1) / sizeof(io32_t)); + + src += (sector_size * p.current_sector); + + for (int i = 0; i < sector_size; i++) { + *dst++ = *src++; + } +} + +static void dd_sector_write (void) { + io32_t *src = DD->SECTOR_BUFFER; + io32_t *dst = p.block_buffer; + + uint8_t sector_size = ((DD->SECTOR_SIZE + 1) / sizeof(io32_t)); + + dst += (sector_size * p.current_sector); + + for (int i = 0; i < sector_size; i++) { + *dst++ = *src++; + } +} + + +void dd_set_disk_state (disk_state_t disk_state) { + uint32_t scr = (DD->SCR & (~(DD_SCR_DISK_CHANGED | DD_SCR_DISK_INSERTED))); + + switch (disk_state) { + case DD_DISK_EJECTED: + break; + case DD_DISK_INSERTED: + scr |= DD_SCR_DISK_INSERTED; + break; + case DD_DISK_CHANGED: + scr |= (DD_SCR_DISK_CHANGED | DD_SCR_DISK_INSERTED); + break; + } + + DD->SCR = scr; +} + +void dd_set_drive_type_development (bool value) { + if (value) { + DD->DRIVE_ID = DD_DRIVE_ID_DEVELOPMENT; + p.is_dev_disk = true; + } else { + DD->DRIVE_ID = DD_DRIVE_ID_RETAIL; + p.is_dev_disk = false; + } +} + +void dd_set_block_ready (bool value) { + p.block_ready = value; +} + +uint32_t dd_get_thb_table_offset (void) { + return (((uint32_t) (p.thb_table)) & 0x0FFFFFFF); +} + + +void dd_init (void) { + DD->SCR = 0; + DD->HEAD_TRACK = 0; + DD->DRIVE_ID = DD_DRIVE_ID_RETAIL; + p.state = STATE_IDLE; + p.track_seek_time = DD_TRACK_SEEK_TIME_TICKS; + p.power_up_delay = true; + p.deffered_cmd_ready = false; + p.bm_running = false; + p.is_dev_disk = false; + p.thb_table = (io32_t *) (DD_THB_TABLE_OFFSET); + p.usb_buffer = (io32_t *) (DD_USB_BUFFER_OFFSET); + p.block_buffer = (io32_t *) (DD_BLOCK_BUFFER_OFFSET); +} + + +void process_dd (void) { + uint32_t scr = DD->SCR; + + if (scr & DD_SCR_HARD_RESET) { + DD->SCR &= ~(DD_SCR_DISK_CHANGED); + DD->HEAD_TRACK = 0; + p.state = STATE_IDLE; + p.power_up_delay = true; + p.deffered_cmd_ready = false; + p.bm_running = false; + } + + if (scr & DD_SCR_CMD_PENDING) { + dd_cmd_t cmd = DD->CMD; + uint16_t data = DD->DATA; + + DD->DATA = data; + + if (p.deffered_cmd_ready) { + if (DD->SEEK_TIMER >= p.next_seek_time) { + p.deffered_cmd_ready = false; + DD->HEAD_TRACK = DD_HEAD_TRACK_INDEX_LOCK | data; + DD->SCR |= DD_SCR_CMD_READY; + } + } else if ((cmd == DD_CMD_SEEK_READ) || (cmd == DD_CMD_SEEK_WRITE)) { + int track_distance = abs((DD->HEAD_TRACK & DD_TRACK_MASK) - (data & DD_TRACK_MASK)); + if (p.power_up_delay) { + p.power_up_delay = false; + p.next_seek_time = DD_POWER_UP_DELAY_TICKS; + } else { + p.next_seek_time = (track_distance * p.track_seek_time); + } + p.deffered_cmd_ready = true; + DD->HEAD_TRACK &= ~(DD_HEAD_TRACK_INDEX_LOCK); + DD->SCR |= DD_SCR_SEEK_TIMER_RESET; + } else { + switch (cmd) { + case DD_CMD_CLEAR_DISK_CHANGE: + DD->SCR &= ~(DD_SCR_DISK_CHANGED); + break; + + case DD_CMD_CLEAR_RESET_STATE: + DD->SCR &= ~(DD_SCR_DISK_CHANGED); + DD->SCR |= DD_SCR_HARD_RESET_CLEAR; + break; + + case DD_CMD_READ_VERSION: + DD->DATA = DD_VERSION_RETAIL; + break; + + case DD_CMD_SET_DISK_TYPE: + break; + + case DD_CMD_REQUEST_STATUS: + DD->DATA = 0; + break; + + case DD_CMD_SET_RTC_YEAR_MONTH: + p.time.year = ((data >> 8) & 0xFF); + p.time.month = (data & 0xFF); + break; + + case DD_CMD_SET_RTC_DAY_HOUR: + p.time.day = ((data >> 8) & 0xFF); + p.time.hour = (data & 0xFF); + break; + + case DD_CMD_SET_RTC_MINUTE_SECOND: + p.time.minute = ((data >> 8) & 0xFF); + p.time.second = (data & 0xFF); + rtc_set_time(&p.time); + break; + + case DD_CMD_GET_RTC_YEAR_MONTH: + DD->DATA = ((p.time.year << 8) | p.time.month); + break; + + case DD_CMD_GET_RTC_DAY_HOUR: + DD->DATA = ((p.time.day << 8) | p.time.hour); + break; + + case DD_CMD_GET_RTC_MINUTE_SECOND: + p.time = *rtc_get_time(); + DD->DATA = ((p.time.minute << 8) | p.time.second); + break; + + case DD_CMD_READ_PROGRAM_VERSION: + DD->DATA = 0; + break; + + default: + break; + } + + DD->SCR |= DD_SCR_CMD_READY; + } + } else { + if (scr & DD_SCR_BM_STOP) { + DD->SCR |= DD_SCR_BM_STOP_CLEAR; + DD->SCR &= ~(DD_SCR_BM_MICRO_ERROR | DD_SCR_BM_TRANSFER_C2 | DD_SCR_BM_TRANSFER_DATA); + p.state = STATE_STOP; + } else if (scr & DD_SCR_BM_START) { + DD->SCR |= DD_SCR_BM_CLEAR | DD_SCR_BM_ACK_CLEAR | DD_SCR_BM_START_CLEAR; + DD->SCR &= ~(DD_SCR_BM_MICRO_ERROR | DD_SCR_BM_TRANSFER_C2 | DD_SCR_BM_TRANSFER_DATA); + p.state = STATE_START; + p.transfer_mode = (scr & DD_SCR_BM_TRANSFER_MODE); + p.full_track_transfer = (scr & DD_SCR_BM_TRANSFER_BLOCKS); + p.starting_block = (DD->SECTOR_NUM == (DD->SECTORS_IN_BLOCK + 1)); + } else if (p.bm_running) { + if (scr & DD_SCR_BM_PENDING) { + DD->SCR |= DD_SCR_BM_CLEAR; + if (p.transfer_mode) { + if (p.current_sector < (DD->SECTORS_IN_BLOCK - 4)) { + p.state = STATE_SECTOR_READ; + } else if (p.current_sector == (DD->SECTORS_IN_BLOCK - 4)) { + p.current_sector += 1; + DD->SCR &= ~(DD_SCR_BM_TRANSFER_DATA); + DD->SCR |= DD_SCR_BM_READY; + } else if (p.current_sector == DD->SECTORS_IN_BLOCK) { + p.state = STATE_NEXT_BLOCK; + } + } else { + if (p.current_sector < (DD->SECTORS_IN_BLOCK - 4)) { + p.state = STATE_SECTOR_WRITE; + } + } + } + if (scr & DD_SCR_BM_ACK) { + DD->SCR |= DD_SCR_BM_ACK_CLEAR; + if (p.transfer_mode) { + if ((p.current_sector <= (DD->SECTORS_IN_BLOCK - 4))) { + } else if (p.current_sector < (DD->SECTORS_IN_BLOCK - 1)) { + p.current_sector += 1; + DD->SCR |= DD_SCR_BM_READY; + } else if (p.current_sector < DD->SECTORS_IN_BLOCK) { + p.current_sector += 1; + DD->SCR |= DD_SCR_BM_TRANSFER_C2 | DD_SCR_BM_READY; + } + } else { + if (p.current_sector == (DD->SECTORS_IN_BLOCK - 4)) { + p.state = STATE_STOP; + } + } + } + } + } + + switch (p.state) { + case STATE_IDLE: + break; + + case STATE_START: + p.bm_running = true; + p.current_sector = 0; + if (p.transfer_mode) { + if (dd_block_valid()) { + p.state = STATE_BLOCK_READ; + DD->SCR |= DD_SCR_BM_TRANSFER_DATA; + } else { + p.state = STATE_SECTOR_READ; + DD->SCR |= DD_SCR_BM_MICRO_ERROR; + } + } else { + p.state = STATE_IDLE; + if (dd_block_valid()) { + DD->SCR |= DD_SCR_BM_TRANSFER_DATA | DD_SCR_BM_READY; + } else { + DD->SCR |= DD_SCR_BM_MICRO_ERROR | DD_SCR_BM_READY; + } + } + break; + + case STATE_BLOCK_READ: + if (dd_block_request()) { + p.state = STATE_BLOCK_READ_WAIT; + } + break; + + case STATE_BLOCK_READ_WAIT: + if (dd_block_ready()) { + p.state = STATE_SECTOR_READ; + } + break; + + case STATE_SECTOR_READ: + dd_sector_read(); + p.state = STATE_IDLE; + p.current_sector += 1; + DD->SCR |= DD_SCR_BM_READY; + break; + + case STATE_SECTOR_WRITE: + dd_sector_write(); + p.current_sector += 1; + if (p.current_sector < (DD->SECTORS_IN_BLOCK - 4)) { + p.state = STATE_IDLE; + DD->SCR |= DD_SCR_BM_READY; + } else { + p.state = STATE_BLOCK_WRITE; + } + break; + + case STATE_BLOCK_WRITE: + if (dd_block_request()) { + p.state = STATE_BLOCK_WRITE_WAIT; + } + break; + + case STATE_BLOCK_WRITE_WAIT: + if (dd_block_ready()) { + p.state = STATE_NEXT_BLOCK; + } + break; + + case STATE_NEXT_BLOCK: + if (p.full_track_transfer) { + p.state = STATE_START; + p.full_track_transfer = false; + p.starting_block = !p.starting_block; + DD->SCR &= ~(DD_SCR_BM_TRANSFER_C2); + } else { + if (p.transfer_mode) { + p.state = STATE_STOP; + } else { + p.state = STATE_IDLE; + DD->SCR &= ~(DD_SCR_BM_TRANSFER_C2 | DD_SCR_BM_TRANSFER_DATA); + DD->SCR |= DD_SCR_BM_READY; + } + } + break; + + case STATE_STOP: + p.state = STATE_IDLE; + p.bm_running = false; + break; + } +} diff --git a/sw/riscv/src/dd.h b/sw/riscv/src/dd.h new file mode 100644 index 0000000..18a79c0 --- /dev/null +++ b/sw/riscv/src/dd.h @@ -0,0 +1,23 @@ +#ifndef DD_H__ +#define DD_H__ + + +#include "sys.h" + + +typedef enum { + DD_DISK_EJECTED, + DD_DISK_INSERTED, + DD_DISK_CHANGED, +} disk_state_t; + + +void dd_set_disk_state (disk_state_t disk_state); +void dd_set_drive_type_development (bool value); +void dd_set_block_ready (bool value); +uint32_t dd_get_thb_table_offset (void); +void dd_init (void); +void process_dd (void); + + +#endif diff --git a/sw/riscv/src/process.c b/sw/riscv/src/process.c index 4f7c2e3..5e899d7 100644 --- a/sw/riscv/src/process.c +++ b/sw/riscv/src/process.c @@ -6,6 +6,7 @@ #include "rtc.h" #include "i2c.h" #include "flashram.h" +#include "dd.h" #include "uart.h" @@ -15,6 +16,7 @@ static const void (*process_table[])(void) = { process_rtc, process_i2c, process_flashram, + process_dd, process_uart, NULL, }; @@ -30,6 +32,7 @@ __attribute__((naked)) void process_loop (void) { rtc_init(); i2c_init(); flashram_init(); + dd_init(); uart_init(); while (1) { diff --git a/sw/riscv/src/sys.h b/sw/riscv/src/sys.h index d454dd7..f0ed845 100644 --- a/sw/riscv/src/sys.h +++ b/sw/riscv/src/sys.h @@ -4,10 +4,17 @@ #include #include +#include #include +#define swap32(x) ((((x) & 0xFF000000UL) >> 24) | \ + (((x) & 0x00FF0000UL) >> 8) | \ + (((x) & 0x0000FF00UL) << 8) | \ + (((x) & 0x000000FFUL) << 24)) + typedef volatile uint8_t io8_t; +typedef volatile uint16_t io16_t; typedef volatile uint32_t io32_t; @@ -189,6 +196,55 @@ typedef volatile struct joybus_regs { #define JOYBUS_SCR_TX_LENGTH_BIT (16) +typedef volatile struct dd_regs { + io32_t SCR; + io16_t DATA; + io8_t CMD; + io8_t __padding_1; + io16_t HEAD_TRACK; + io16_t __padding_2; + io8_t SECTOR_NUM; + io8_t SECTOR_SIZE; + io8_t SECTOR_SIZE_FULL; + io8_t SECTORS_IN_BLOCK; + io16_t DRIVE_ID; + io16_t __padding_3; + io32_t SEEK_TIMER; + io32_t __padding_4[58]; + io32_t SECTOR_BUFFER[64]; +} dd_regs_t; + +#define DD_BASE (0xB0000000UL) +#define DD ((dd_regs_t *) DD_BASE) + +#define DD_SCR_HARD_RESET (1 << 0) +#define DD_SCR_HARD_RESET_CLEAR (1 << 1) +#define DD_SCR_CMD_PENDING (1 << 2) +#define DD_SCR_CMD_READY (1 << 3) +#define DD_SCR_BM_PENDING (1 << 4) +#define DD_SCR_BM_READY (1 << 5) +#define DD_SCR_DISK_INSERTED (1 << 6) +#define DD_SCR_DISK_CHANGED (1 << 7) +#define DD_SCR_BM_START (1 << 8) +#define DD_SCR_BM_START_CLEAR (1 << 9) +#define DD_SCR_BM_STOP (1 << 10) +#define DD_SCR_BM_STOP_CLEAR (1 << 11) +#define DD_SCR_BM_TRANSFER_MODE (1 << 12) +#define DD_SCR_BM_TRANSFER_BLOCKS (1 << 13) +#define DD_SCR_BM_TRANSFER_DATA (1 << 14) +#define DD_SCR_BM_TRANSFER_C2 (1 << 15) +#define DD_SCR_BM_MICRO_ERROR (1 << 16) +#define DD_SCR_BM_ACK (1 << 17) +#define DD_SCR_BM_ACK_CLEAR (1 << 18) +#define DD_SCR_BM_CLEAR (1 << 19) +#define DD_SCR_SEEK_TIMER_RESET (1 << 20) + +#define DD_TRACK_MASK (0x0FFF) +#define DD_HEAD_MASK (0x1000) +#define DD_HEAD_TRACK_MASK (DD_HEAD_MASK | DD_TRACK_MASK) +#define DD_HEAD_TRACK_INDEX_LOCK (1 << 13) + + void reset_handler(void); diff --git a/sw/riscv/src/usb.c b/sw/riscv/src/usb.c index d7713c3..bb66172 100644 --- a/sw/riscv/src/usb.c +++ b/sw/riscv/src/usb.c @@ -151,7 +151,7 @@ void usb_debug_reset (void) { p.debug_rx_busy = false; p.debug_tx_busy = false; - USB->SCR = USB_SCR_FLUSH_TX | USB_SCR_FLUSH_RX; + USB->SCR = USB_SCR_ENABLED | USB_SCR_FLUSH_TX | USB_SCR_FLUSH_RX; } static uint8_t rx_cmd_current_byte = 0;