[SC64][FW][SW] Added 64DD implementation with USB streaming (#14)

This commit is contained in:
Mateusz Faderewski 2021-12-24 23:51:30 +01:00 committed by GitHub
parent 0929dbeff4
commit 92e5c5747b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1371 additions and 85 deletions

View File

@ -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

View File

@ -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

141
fw/rtl/cpu/cpu_dd.sv Normal file
View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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 (

View File

@ -21,6 +21,7 @@ package sc64;
ID_CPU_SDRAM,
ID_CPU_FLASHRAM,
ID_CPU_SI,
ID_CPU_DD,
__ID_CPU_END
} e_cpu_id;

View File

@ -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;

View File

@ -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);
}

View File

@ -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")

View File

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

View File

@ -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;
}
}

440
sw/riscv/src/dd.c Normal file
View File

@ -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;
}
}

23
sw/riscv/src/dd.h Normal file
View File

@ -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

View File

@ -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) {

View File

@ -4,10 +4,17 @@
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#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);

View File

@ -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;