diff --git a/fw/project/lcmxo2/.gitignore b/fw/project/lcmxo2/.gitignore
index c5bd274..aa4d819 100644
--- a/fw/project/lcmxo2/.gitignore
+++ b/fw/project/lcmxo2/.gitignore
@@ -1,6 +1,7 @@
.recovery
*.dir/
*.ccl
+*.dmp
*.html
*.ini
*.rva
diff --git a/fw/project/lcmxo2/sc64.ldf b/fw/project/lcmxo2/sc64.ldf
index 9bdab48..d9473c2 100644
--- a/fw/project/lcmxo2/sc64.ldf
+++ b/fw/project/lcmxo2/sc64.ldf
@@ -66,7 +66,13 @@
-
+
+
+
+
+
+
+
diff --git a/fw/rtl/mcu/mcu_top.sv b/fw/rtl/mcu/mcu_top.sv
index bf1b9a1..11ef5cc 100644
--- a/fw/rtl/mcu/mcu_top.sv
+++ b/fw/rtl/mcu/mcu_top.sv
@@ -322,7 +322,6 @@ module mcu_top (
// Register list
typedef enum bit [7:0] {
- REG_STATUS,
REG_MEM_ADDRESS,
REG_MEM_SCR,
REG_USB_SCR,
@@ -373,15 +372,6 @@ module mcu_top (
reg_rdata <= 32'd0;
case (address)
- REG_STATUS: begin
- reg_rdata <= {
- 29'd0,
- n64_scb.cfg_pending,
- ~sd_det_ff[2],
- ~button_ff[2]
- };
- end
-
REG_MEM_ADDRESS: begin
reg_rdata <= mem_address;
end
@@ -396,7 +386,10 @@ module mcu_top (
REG_USB_SCR: begin
reg_rdata <= {
- 28'd0,
+ 4'd0,
+ usb_scb.tx_count,
+ usb_scb.rx_count,
+ 2'b00,
usb_scb.reset_pending,
~fifo_bus.tx_full,
~fifo_bus.rx_empty,
@@ -429,7 +422,8 @@ module mcu_top (
REG_CFG_SCR: begin
reg_rdata <= {
- 20'd0,
+ ~button_ff[2],
+ 19'd0,
n64_scb.rom_extended_enabled,
n64_scb.eeprom_16k_mode,
n64_scb.eeprom_enabled,
@@ -455,7 +449,8 @@ module mcu_top (
REG_CFG_CMD: begin
reg_rdata <= {
- 24'd0,
+ 23'd0,
+ n64_scb.cfg_pending,
n64_scb.cfg_cmd
};
end
@@ -510,11 +505,57 @@ module mcu_top (
REG_SD_SCR: begin
reg_rdata <= {
- 30'd0,
+ 4'd0,
+ sd_scb.tx_count,
+ sd_scb.rx_count,
+ ~sd_det_ff[2],
+ sd_scb.card_busy,
+ sd_scb.cmd_error,
+ sd_scb.cmd_busy,
sd_scb.clock_mode
};
end
+ REG_SD_ARG: begin
+ reg_rdata <= sd_scb.cmd_arg;
+ end
+
+ REG_SD_CMD: begin
+ reg_rdata <= {
+ 22'd0,
+ sd_scb.cmd_ignore_crc,
+ sd_scb.cmd_long_response,
+ sd_scb.cmd_reserved_response,
+ sd_scb.cmd_skip_response,
+ sd_scb.cmd_index
+ };
+ end
+
+ REG_SD_RSP_0: begin
+ reg_rdata <= sd_scb.cmd_rsp[31:0];
+ end
+
+ REG_SD_RSP_1: begin
+ reg_rdata <= sd_scb.cmd_rsp[63:32];
+ end
+
+ REG_SD_RSP_2: begin
+ reg_rdata <= sd_scb.cmd_rsp[95:64];
+ end
+
+ REG_SD_RSP_3: begin
+ reg_rdata <= sd_scb.cmd_rsp[127:96];
+ end
+
+ REG_SD_DAT: begin
+ reg_rdata <= {
+ 18'd0,
+ sd_scb.dat_error,
+ sd_scb.dat_busy,
+ 12'd0
+ };
+ end
+
REG_SD_DMA_ADDRESS: begin
reg_rdata <= {
5'd0,
@@ -604,6 +645,12 @@ module mcu_top (
usb_dma_scb.start <= 1'b0;
usb_dma_scb.stop <= 1'b0;
+ sd_scb.cmd_start <= 1'b0;
+ sd_scb.dat_fifo_flush <= 1'b0;
+ sd_scb.dat_start_write <= 1'b0;
+ sd_scb.dat_start_read <= 1'b0;
+ sd_scb.dat_stop <= 1'b0;
+
sd_dma_scb.start <= 1'b0;
sd_dma_scb.stop <= 1'b0;
@@ -656,8 +703,6 @@ module mcu_top (
n64_scb.rtc_wdata_valid <= 1'b0;
end else if (reg_write) begin
case (address)
- REG_STATUS: begin end
-
REG_MEM_ADDRESS: begin
mem_address <= reg_wdata;
end
@@ -725,7 +770,7 @@ module mcu_top (
n64_scb.cfg_irq,
n64_scb.cfg_error,
n64_scb.cfg_done
- } <= reg_wdata[2:0];
+ } <= reg_wdata[11:9];
end
REG_FLASHRAM_SCR: begin
@@ -760,6 +805,27 @@ module mcu_top (
sd_scb.clock_mode <= reg_wdata[1:0];
end
+ REG_SD_ARG: begin
+ sd_scb.cmd_arg <= reg_wdata;
+ end
+
+ REG_SD_CMD: begin
+ sd_scb.cmd_start <= 1'b1;
+ sd_scb.cmd_ignore_crc <= reg_wdata[9];
+ sd_scb.cmd_long_response <= reg_wdata[8];
+ sd_scb.cmd_reserved_response <= reg_wdata[7];
+ sd_scb.cmd_skip_response <= reg_wdata[6];
+ sd_scb.cmd_index <= reg_wdata[5:0];
+ end
+
+ REG_SD_DAT: begin
+ sd_scb.dat_blocks <= reg_wdata[11:4];
+ sd_scb.dat_stop <= reg_wdata[3];
+ sd_scb.dat_start_read <= reg_wdata[2];
+ sd_scb.dat_start_write <= reg_wdata[1];
+ sd_scb.dat_fifo_flush <= reg_wdata[0];
+ end
+
REG_SD_DMA_ADDRESS: begin
sd_dma_scb.starting_address <= reg_wdata[26:0];
end
diff --git a/fw/rtl/memory/memory_dma.sv b/fw/rtl/memory/memory_dma.sv
index 0d7761d..fcba0bd 100644
--- a/fw/rtl/memory/memory_dma.sv
+++ b/fw/rtl/memory/memory_dma.sv
@@ -76,6 +76,7 @@ module memory_dma (
always_ff @(posedge clk) begin
rx_rdata_pop <= (
!rx_rdata_pop &&
+ !fifo_bus.rx_read &&
trx_enabled &&
rx_buffer_counter < 2'd2 &&
!fifo_bus.rx_empty &&
diff --git a/fw/rtl/n64/n64_cfg.sv b/fw/rtl/n64/n64_cfg.sv
index 70dd91f..0c687b2 100644
--- a/fw/rtl/n64/n64_cfg.sv
+++ b/fw/rtl/n64/n64_cfg.sv
@@ -31,7 +31,8 @@ module n64_cfg (
REG_STATUS: reg_bus.rdata = {
n64_scb.cfg_pending,
cfg_error,
- 14'd0
+ irq,
+ 13'd0
};
REG_COMMAND: reg_bus.rdata = {8'd0, n64_scb.cfg_cmd};
REG_DATA_0_H: reg_bus.rdata = n64_scb.cfg_wdata[0][31:16];
diff --git a/fw/rtl/n64/n64_top.sv b/fw/rtl/n64/n64_top.sv
index c63eafc..0191115 100644
--- a/fw/rtl/n64/n64_top.sv
+++ b/fw/rtl/n64/n64_top.sv
@@ -23,8 +23,13 @@ module n64_top (
logic n64_dd_irq;
logic n64_cfg_irq;
+ logic n64_irq_oe;
- assign n64_irq = (n64_dd_irq || n64_cfg_irq) ? 1'b0 : 1'bZ;
+ always @(posedge clk) begin
+ n64_irq_oe <= (n64_dd_irq || n64_cfg_irq);
+ end
+
+ assign n64_irq = n64_irq_oe ? 1'b0 : 1'bZ;
n64_reg_bus reg_bus ();
diff --git a/fw/rtl/sd/sd_cmd.sv b/fw/rtl/sd/sd_cmd.sv
index 661102f..e668c5c 100644
--- a/fw/rtl/sd/sd_cmd.sv
+++ b/fw/rtl/sd/sd_cmd.sv
@@ -10,6 +10,222 @@ module sd_cmd (
inout sd_cmd
);
- assign sd_cmd = 1'bZ;
+ // Input and output data sampling
+
+ logic sd_cmd_oe;
+ logic sd_cmd_out;
+ logic sd_cmd_in;
+ logic sd_cmd_oe_data;
+ logic sd_cmd_data;
+
+ assign sd_cmd = sd_cmd_oe ? sd_cmd_out : 1'bZ;
+
+ always_ff @(posedge clk) begin
+ sd_cmd_oe <= sd_cmd_oe_data;
+ sd_cmd_out <= sd_cmd_data;
+ sd_cmd_in <= sd_cmd;
+ end
+
+
+ // CMD state
+
+ typedef enum bit [1:0] {
+ STATE_IDLE,
+ STATE_TX,
+ STATE_WAIT,
+ STATE_RX
+ } e_state;
+
+ e_state state;
+ e_state next_state;
+
+ always_ff @(posedge clk) begin
+ if (reset) begin
+ state <= STATE_IDLE;
+ end else begin
+ state <= next_state;
+ end
+ end
+
+ assign sd_scb.cmd_busy = (state != STATE_IDLE);
+
+ logic [7:0] counter;
+
+ always_comb begin
+ next_state = state;
+
+ case (state)
+ STATE_IDLE: begin
+ if (sd_scb.cmd_start) begin
+ next_state = STATE_TX;
+ end
+ end
+
+ STATE_TX: begin
+ if (sd_clk_falling) begin
+ if (counter == 8'd48) begin
+ if (sd_scb.cmd_skip_response) begin
+ next_state = STATE_IDLE;
+ end else begin
+ next_state = STATE_WAIT;
+ end
+ end
+ end
+ end
+
+ STATE_WAIT: begin
+ if (sd_clk_rising) begin
+ if (counter == 8'd64) begin
+ next_state = STATE_IDLE;
+ end
+ if (!sd_cmd_in) begin
+ next_state = STATE_RX;
+ end
+ end
+ end
+
+ STATE_RX: begin
+ if (sd_clk_rising) begin
+ if (sd_scb.cmd_long_response) begin
+ if (counter == 8'd136) begin
+ next_state = STATE_IDLE;
+ end
+ end else begin
+ if (counter == 8'd48) begin
+ next_state = STATE_IDLE;
+ end
+ end
+ end
+ end
+
+ default: begin
+ next_state = STATE_IDLE;
+ end
+ endcase
+ end
+
+
+ // CRC7 unit
+
+ logic crc_reset;
+ logic crc_enable;
+ logic crc_data;
+ logic [6:0] crc_result;
+
+ sd_crc_7 sd_crc_7_inst (
+ .clk(clk),
+ .reset(crc_reset),
+ .enable(crc_enable),
+ .data(crc_data),
+ .result(crc_result)
+ );
+
+
+ // Data shifting
+
+ logic [7:0] data_shift;
+
+ assign crc_data = (state == STATE_RX) ? data_shift[0] : data_shift[7];
+
+ always_ff @(posedge clk) begin
+ crc_reset <= 1'b0;
+ crc_enable <= 1'b0;
+
+ if (reset) begin
+ sd_cmd_oe_data <= 1'b0;
+ sd_cmd_data <= 1'b1;
+ end else begin
+ case (state)
+ STATE_IDLE: begin
+ if (sd_scb.cmd_start) begin
+ sd_scb.cmd_error <= 1'b0;
+ crc_reset <= 1'b1;
+ data_shift <= {2'b01, sd_scb.cmd_index};
+ counter <= 8'd0;
+ end
+ end
+
+ STATE_TX: begin
+ if (sd_clk_falling) begin
+ sd_cmd_oe_data <= 1'b1;
+ sd_cmd_data <= data_shift[7];
+ counter <= counter + 1'd1;
+ crc_enable <= 1'b1;
+ data_shift <= {data_shift[6:0], 1'bX};
+ if (counter == 8'd7) begin
+ data_shift <= sd_scb.cmd_arg[31:24];
+ end
+ if (counter == 8'd15) begin
+ data_shift <= sd_scb.cmd_arg[23:16];
+ end
+ if (counter == 8'd23) begin
+ data_shift <= sd_scb.cmd_arg[15:8];
+ end
+ if (counter == 8'd31) begin
+ data_shift <= sd_scb.cmd_arg[7:0];
+ end
+ if (counter == 8'd39) begin
+ data_shift <= {crc_result, 1'b1};
+ end
+ if (counter == 8'd48) begin
+ sd_cmd_oe_data <= 1'b0;
+ counter <= 8'd0;
+ end
+ end
+ end
+
+ STATE_WAIT: begin
+ if (sd_clk_rising) begin
+ counter <= counter + 1'd1;
+ if (counter == 8'd64) begin
+ sd_scb.cmd_error <= 1'b1;
+ end
+ if (!sd_cmd_in) begin
+ counter <= 8'd1;
+ crc_reset <= 1'b1;
+ data_shift <= 8'h00;
+ end
+ end
+ end
+
+ STATE_RX: begin
+ if (sd_clk_rising) begin
+ counter <= counter + 1'd1;
+ data_shift <= {data_shift[6:0], sd_cmd_in};
+ if (counter == 8'd8) begin
+ if (data_shift[6:0] != (sd_scb.cmd_reserved_response ? 7'h3F : {1'b0, sd_scb.cmd_index})) begin
+ sd_scb.cmd_error <= 1'b1;
+ end
+ end
+ if (sd_scb.cmd_long_response) begin
+ if (counter >= 8'd8 && counter < 8'd128) begin
+ crc_enable <= 1'b1;
+ end
+ if (counter[2:0] == 3'd0) begin
+ sd_scb.cmd_rsp <= {sd_scb.cmd_rsp[119:0], data_shift};
+ end
+ if (!sd_scb.cmd_ignore_crc && counter == 8'd136) begin
+ if (data_shift[7:1] != crc_result) begin
+ sd_scb.cmd_error <= 1'b1;
+ end
+ end
+ end else begin
+ if (counter < 8'd40) begin
+ crc_enable <= 1'b1;
+ end
+ if (counter <= 8'd40 && counter[2:0] == 3'd0) begin
+ sd_scb.cmd_rsp <= {sd_scb.cmd_rsp[119:0], data_shift};
+ end
+ if (!sd_scb.cmd_ignore_crc && counter == 8'd48) begin
+ if (data_shift[7:1] != crc_result) begin
+ sd_scb.cmd_error <= 1'b1;
+ end
+ end
+ end
+ end
+ end
+ endcase
+ end
+ end
endmodule
diff --git a/fw/rtl/sd/sd_crc_16.sv b/fw/rtl/sd/sd_crc_16.sv
new file mode 100644
index 0000000..77fe8be
--- /dev/null
+++ b/fw/rtl/sd/sd_crc_16.sv
@@ -0,0 +1,33 @@
+module sd_crc_16 (
+ input clk,
+ input reset,
+
+ input enable,
+ input shift,
+ input data,
+
+ output logic [15:0] result
+);
+
+ logic crc_inv;
+
+ assign crc_inv = result[15] ^ data;
+
+ always_ff @(posedge clk) begin
+ if (reset) begin
+ result <= 16'd0;
+ end else if (enable) begin
+ result <= {
+ result[14:12],
+ result[11] ^ crc_inv,
+ result[10:5],
+ result[4] ^ crc_inv,
+ result[3:0],
+ crc_inv
+ };
+ end else if (shift) begin
+ result <= {result[14:0], 1'b1};
+ end
+ end
+
+endmodule
diff --git a/fw/rtl/sd/sd_dat.sv b/fw/rtl/sd/sd_dat.sv
new file mode 100644
index 0000000..26ace97
--- /dev/null
+++ b/fw/rtl/sd/sd_dat.sv
@@ -0,0 +1,246 @@
+module sd_dat (
+ input clk,
+ input reset,
+
+ sd_scb.dat sd_scb,
+
+ fifo_bus.fifo fifo_bus,
+
+ input sd_clk_rising,
+ input sd_clk_falling,
+
+ inout [3:0] sd_dat
+);
+
+ // Input and output data sampling
+
+ logic sd_dat_oe;
+ logic sd_dat_out;
+ logic [3:0] sd_dat_in;
+ logic sd_dat_oe_data;
+ logic [3:0] sd_dat_data;
+
+ assign sd_dat = sd_dat_oe ? sd_dat_out : 4'hZ;
+
+ always_ff @(posedge clk) begin
+ sd_dat_oe <= sd_dat_oe_data;
+ sd_dat_out <= sd_dat_data;
+ sd_dat_in <= sd_dat;
+ end
+
+ always_ff @(posedge clk) begin
+ sd_scb.card_busy <= !sd_dat_in[0];
+ end
+
+
+ // FIFO
+
+ logic rx_full;
+ logic rx_almost_full;
+ logic rx_write;
+ logic [7:0] rx_wdata;
+
+ logic tx_empty;
+ logic tx_almost_empty;
+ logic tx_read;
+ logic [7:0] tx_rdata;
+
+ fifo_8kb fifo_8kb_rx_inst (
+ .clk(clk),
+ .reset(reset || sd_scb.dat_fifo_flush),
+
+ .empty(fifo_bus.rx_empty),
+ .almost_empty(fifo_bus.rx_almost_empty),
+ .read(fifo_bus.rx_read),
+ .rdata(fifo_bus.rx_rdata),
+
+ .full(rx_full),
+ .almost_full(rx_almost_full),
+ .write(rx_write),
+ .wdata(rx_wdata),
+
+ .count(sd_scb.rx_count)
+ );
+
+ fifo_8kb fifo_8kb_tx_inst (
+ .clk(clk),
+ .reset(reset || sd_scb.dat_fifo_flush),
+
+ .empty(tx_empty),
+ .almost_empty(tx_almost_empty),
+ .read(tx_read),
+ .rdata(tx_rdata),
+
+ .full(fifo_bus.tx_full),
+ .almost_full(fifo_bus.tx_almost_full),
+ .write(fifo_bus.tx_write),
+ .wdata(fifo_bus.tx_wdata),
+
+ .count(sd_scb.tx_count)
+ );
+
+
+ // DAT state
+
+ typedef enum bit [1:0] {
+ STATE_IDLE,
+ STATE_RX_WAIT,
+ STATE_RX
+ } e_state;
+
+ e_state state;
+ e_state next_state;
+
+ always_ff @(posedge clk) begin
+ if (reset || sd_scb.dat_stop) begin
+ state <= STATE_IDLE;
+ end else begin
+ state <= next_state;
+ end
+ end
+
+ assign sd_scb.dat_busy = (state != STATE_IDLE);
+
+ logic [10:0] counter;
+ logic [7:0] blocks_remaining;
+
+ always_comb begin
+ next_state = state;
+
+ case (state)
+ STATE_IDLE: begin
+ if (sd_scb.dat_start_read) begin
+ next_state = STATE_RX_WAIT;
+ end
+ if (sd_scb.dat_start_write) begin
+ end
+ end
+
+ STATE_RX_WAIT: begin
+ if (sd_clk_rising) begin
+ if (!sd_dat_in[0]) begin
+ next_state = STATE_RX;
+ end
+ end
+ end
+
+ STATE_RX: begin
+ if (sd_clk_rising) begin
+ if (counter == 11'd1041) begin
+ if (blocks_remaining == 8'd0) begin
+ next_state = STATE_IDLE;
+ end else begin
+ next_state = STATE_RX_WAIT;
+ end
+ end
+ end
+ end
+ endcase
+ end
+
+
+ // CRC16 units
+
+ logic crc_reset;
+ logic crc_enable;
+ logic crc_shift;
+ logic [3:0] crc_data;
+ logic [15:0] crc_result [0:3];
+
+ sd_crc_16 sd_crc_16_inst_0 (
+ .clk(clk),
+ .reset(crc_reset),
+ .enable(crc_enable),
+ .shift(crc_shift),
+ .data(crc_data[0]),
+ .result(crc_result[0])
+ );
+
+ sd_crc_16 sd_crc_16_inst_1 (
+ .clk(clk),
+ .reset(crc_reset),
+ .enable(crc_enable),
+ .shift(crc_shift),
+ .data(crc_data[1]),
+ .result(crc_result[1])
+ );
+
+ sd_crc_16 sd_crc_16_inst_2 (
+ .clk(clk),
+ .reset(crc_reset),
+ .enable(crc_enable),
+ .shift(crc_shift),
+ .data(crc_data[2]),
+ .result(crc_result[2])
+ );
+
+ sd_crc_16 sd_crc_16_inst_3 (
+ .clk(clk),
+ .reset(crc_reset),
+ .enable(crc_enable),
+ .shift(crc_shift),
+ .data(crc_data[3]),
+ .result(crc_result[3])
+ );
+
+
+ // Data shifting
+
+ assign crc_data = rx_wdata[3:0];
+
+ always_ff @(posedge clk) begin
+ rx_write <= 1'b0;
+ crc_reset <= 1'b0;
+ crc_enable <= 1'b0;
+ crc_shift <= 1'b0;
+
+ if (reset || sd_scb.dat_stop) begin
+ sd_dat_oe_data <= 1'b0;
+ sd_dat_data <= 4'hF;
+ end else begin
+ case (state)
+ STATE_IDLE: begin
+ if (sd_scb.dat_start_read || sd_scb.dat_start_write) begin
+ sd_scb.dat_error <= 1'b0;
+ blocks_remaining <= sd_scb.dat_blocks;
+ end
+ end
+
+ STATE_RX_WAIT: begin
+ if (sd_clk_rising) begin
+ if (!sd_dat_in[0]) begin
+ counter <= 8'd1;
+ crc_reset <= 1'b1;
+ end
+ end
+ end
+
+ STATE_RX: begin
+ if (sd_clk_rising) begin
+ counter <= counter + 1'd1;
+ rx_wdata <= {rx_wdata[3:0], sd_dat_in};
+ if (counter <= 11'd1024) begin
+ crc_enable <= 1'b1;
+ if (!counter[0]) begin
+ if (rx_full) begin
+ sd_scb.dat_error <= 1'b1;
+ end else begin
+ rx_write <= 1'b1;
+ end
+ end
+ end else begin
+ crc_shift <= 1'b1;
+ if ({crc_result[3][15], crc_result[2][15], crc_result[1][15], crc_result[0][15]} != sd_dat_in) begin
+ sd_scb.dat_error <= 1'b1;
+ end
+ end
+ if (counter == 11'd1041) begin
+ blocks_remaining <= blocks_remaining - 1'd1;
+ end
+ end
+ end
+ endcase
+ end
+ end
+
+endmodule
diff --git a/fw/rtl/sd/sd_scb.sv b/fw/rtl/sd/sd_scb.sv
index 1a4d37a..3309cf9 100644
--- a/fw/rtl/sd/sd_scb.sv
+++ b/fw/rtl/sd/sd_scb.sv
@@ -2,12 +2,56 @@ interface sd_scb ();
logic [1:0] clock_mode;
- logic [5:0] index;
+ logic card_busy;
+
+ logic [10:0] rx_count;
+ logic [10:0] tx_count;
+
+ logic [5:0] cmd_index;
+ logic [31:0] cmd_arg;
+ logic [127:0] cmd_rsp;
+ logic cmd_start;
+ logic cmd_skip_response;
+ logic cmd_reserved_response;
+ logic cmd_long_response;
+ logic cmd_ignore_crc;
+ logic cmd_busy;
+ logic cmd_error;
+
+ logic dat_fifo_flush;
+ logic dat_start_write;
+ logic dat_start_read;
+ logic dat_stop;
+ logic [7:0] dat_blocks;
+ logic dat_busy;
+ logic dat_error;
modport controller (
output clock_mode,
- output index
+ input card_busy,
+
+ input rx_count,
+ input tx_count,
+
+ output cmd_index,
+ output cmd_arg,
+ input cmd_rsp,
+ output cmd_start,
+ output cmd_skip_response,
+ output cmd_reserved_response,
+ output cmd_long_response,
+ output cmd_ignore_crc,
+ input cmd_busy,
+ input cmd_error,
+
+ output dat_fifo_flush,
+ output dat_start_write,
+ output dat_start_read,
+ output dat_stop,
+ output dat_blocks,
+ input dat_busy,
+ input dat_error
);
modport clk (
@@ -15,7 +59,31 @@ interface sd_scb ();
);
modport cmd (
- input index
+ input cmd_index,
+ input cmd_arg,
+ output cmd_rsp,
+ input cmd_start,
+ input cmd_skip_response,
+ input cmd_reserved_response,
+ input cmd_long_response,
+ input cmd_ignore_crc,
+ output cmd_busy,
+ output cmd_error
+ );
+
+ modport dat (
+ output card_busy,
+
+ output rx_count,
+ output tx_count,
+
+ input dat_fifo_flush,
+ input dat_start_write,
+ input dat_start_read,
+ input dat_stop,
+ input dat_blocks,
+ output dat_busy,
+ output dat_error
);
endinterface
diff --git a/fw/rtl/sd/sd_top.sv b/fw/rtl/sd/sd_top.sv
index 42b2e46..a5bccde 100644
--- a/fw/rtl/sd/sd_top.sv
+++ b/fw/rtl/sd/sd_top.sv
@@ -11,8 +11,6 @@ module sd_top (
inout [3:0] sd_dat
);
- assign sd_dat = 4'hZ;
-
logic sd_clk_rising;
logic sd_clk_falling;
@@ -40,4 +38,18 @@ module sd_top (
.sd_cmd(sd_cmd)
);
+ sd_dat sd_dat_inst (
+ .clk(clk),
+ .reset(reset),
+
+ .sd_scb(sd_scb),
+
+ .fifo_bus(fifo_bus),
+
+ .sd_clk_rising(sd_clk_rising),
+ .sd_clk_falling(sd_clk_falling),
+
+ .sd_dat(sd_dat)
+ );
+
endmodule
diff --git a/fw/rtl/usb/usb_ft1248.sv b/fw/rtl/usb/usb_ft1248.sv
index aad5b0f..5251c10 100644
--- a/fw/rtl/usb/usb_ft1248.sv
+++ b/fw/rtl/usb/usb_ft1248.sv
@@ -4,19 +4,25 @@ interface usb_scb ();
logic reset_pending;
logic reset_ack;
logic write_buffer_flush;
+ logic [10:0] rx_count;
+ logic [10:0] tx_count;
modport controller (
output fifo_flush,
input reset_pending,
output reset_ack,
- output write_buffer_flush
+ output write_buffer_flush,
+ input rx_count,
+ input tx_count
);
modport usb (
input fifo_flush,
output reset_pending,
input reset_ack,
- input write_buffer_flush
+ input write_buffer_flush,
+ output rx_count,
+ output tx_count
);
endinterface
@@ -59,7 +65,9 @@ module usb_ft1248 (
.full(rx_full),
.almost_full(rx_almost_full),
.write(rx_write),
- .wdata(rx_wdata)
+ .wdata(rx_wdata),
+
+ .count(usb_scb.rx_count)
);
fifo_8kb fifo_8kb_tx_inst (
@@ -74,7 +82,9 @@ module usb_ft1248 (
.full(fifo_bus.tx_full),
.almost_full(fifo_bus.tx_almost_full),
.write(fifo_bus.tx_write),
- .wdata(fifo_bus.tx_wdata)
+ .wdata(fifo_bus.tx_wdata),
+
+ .count(usb_scb.tx_count)
);
logic [1:0] usb_pwrsav_ff;
diff --git a/fw/rtl/vendor/lcmxo2/fifo_8kb.sv b/fw/rtl/vendor/lcmxo2/fifo_8kb.sv
index a8ef3a7..db02ad2 100644
--- a/fw/rtl/vendor/lcmxo2/fifo_8kb.sv
+++ b/fw/rtl/vendor/lcmxo2/fifo_8kb.sv
@@ -10,7 +10,9 @@ module fifo_8kb (
output full,
output almost_full,
input write,
- input [7:0] wdata
+ input [7:0] wdata,
+
+ output logic [10:0] count
);
fifo_8kb_lattice_generated fifo_8kb_lattice_generated_inst (
@@ -28,4 +30,18 @@ module fifo_8kb (
.AlmostFull(almost_full)
);
+ always_ff @(posedge clk) begin
+ if (reset) begin
+ count <= 11'd0;
+ end else begin
+ if (write && read) begin
+ count <= count;
+ end else if (write) begin
+ count <= count + 1'd1;
+ end else if (read) begin
+ count <= count - 1'd1;
+ end
+ end
+ end
+
endmodule
diff --git a/sw/bootloader/src/fatfs/diskio.c b/sw/bootloader/src/fatfs/diskio.c
index 2a442c1..9d61a65 100644
--- a/sw/bootloader/src/fatfs/diskio.c
+++ b/sw/bootloader/src/fatfs/diskio.c
@@ -1,36 +1,60 @@
#include "ff.h"
#include "diskio.h"
+#include "../io.h"
#include "../sc64.h"
+#include "../error.h"
-
-// DSTATUS status[__DRIVE_COUNT] = { STA_NOINIT, STA_NOINIT };
+DSTATUS status = STA_NOINIT;
DSTATUS disk_status (BYTE pdrv) {
- // if (pdrv >= __DRIVE_COUNT) {
+ if (pdrv > 0) {
return STA_NODISK;
- // }
- // return status[pdrv];
+ }
+ return status;
}
DSTATUS disk_initialize (BYTE pdrv) {
- // if (pdrv >= __DRIVE_COUNT) {
+ if (pdrv > 0) {
return STA_NODISK;
- // }
- // if (!sc64_storage_init((drive_id_t) (pdrv))) {
- // status[pdrv] &= ~(STA_NOINIT);
- // }
- // return status[pdrv];
+ }
+ if (!sc64_sd_card_initialize()) {
+ status &= ~(STA_NOINIT);
+ }
+ return status;
}
DRESULT disk_read (BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
- // if (pdrv >= __DRIVE_COUNT) {
- // return RES_PARERR;
- // }
- // if (sc64_storage_read((drive_id_t) (pdrv), buff, sector, count)) {
- return RES_ERROR;
- // }
- // return RES_OK;
+ if (pdrv > 0) {
+ return RES_PARERR;
+ }
+ uint32_t physical_address = ((uint32_t) (buff)) & 0x1FFFFFFF;
+ uint32_t block = 0;
+ while (count > 0) {
+ if (physical_address < (8 * 1024 * 1024)) {
+ block = ((count > 16) ? 16 : count);
+ if (sc64_sd_read_sectors(sector, 0x1FFE0000UL, block * 512)) {
+ return RES_ERROR;
+ }
+ for (uint32_t i = 0; i < (block * 512); i += 4) {
+ uint32_t data = pi_io_read((uint32_t *) (0x1FFE0000UL + i));
+ uint8_t *ptr = (uint8_t *) (&data);
+ for (int j = 0; j < 4; j++) {
+ *buff++ = *ptr++;
+ }
+ }
+ } else {
+ block = ((count > 256) ? 256 : count);
+ if (sc64_sd_read_sectors(sector, physical_address, block * 512)) {
+ error_display("Kurwa 0x%08X, 0x%08X\n", physical_address, block);
+ return RES_ERROR;
+ }
+ physical_address += (block * 512);
+ }
+ count -= block;
+ sector += block;
+ }
+ return RES_OK;
}
#if !FF_FS_READONLY
diff --git a/sw/bootloader/src/io.h b/sw/bootloader/src/io.h
index b1435ad..542b924 100644
--- a/sw/bootloader/src/io.h
+++ b/sw/bootloader/src/io.h
@@ -246,6 +246,7 @@ typedef struct {
#define SC64_REGS_BASE (0x1FFF0000UL)
#define SC64_REGS ((sc64_regs_t *) SC64_REGS_BASE)
+#define SC64_SR_IRQ_PENDING (1 << 29)
#define SC64_SR_CMD_ERROR (1 << 30)
#define SC64_SR_CPU_BUSY (1 << 31)
diff --git a/sw/bootloader/src/sc64.c b/sw/bootloader/src/sc64.c
index e601146..f1e674b 100644
--- a/sw/bootloader/src/sc64.c
+++ b/sw/bootloader/src/sc64.c
@@ -15,6 +15,10 @@ typedef enum {
SC64_CMD_USB_WRITE = 'M',
SC64_CMD_USB_READ_STATUS = 'u',
SC64_CMD_USB_READ = 'm',
+ SC64_CMD_SD_CARD_INITIALIZE = 'i',
+ SC64_CMD_SD_SECTOR_SET = 'I',
+ SC64_CMD_SD_READ = 's',
+ SC64_CMD_SD_WRITE = 'S',
} cmd_id_t;
@@ -136,3 +140,19 @@ bool sc64_usb_read (uint32_t *address, uint32_t length) {
} while(result[0] & (1 << 24));
return false;
}
+
+bool sc64_sd_card_initialize (void) {
+ if (sc64_execute_cmd(SC64_CMD_SD_CARD_INITIALIZE, NULL, NULL)) {
+ return true;
+ }
+ return false;
+}
+
+bool sc64_sd_read_sectors (uint32_t starting_sector, uint32_t address, uint32_t length) {
+ uint32_t sector_set_args[2] = { starting_sector, 0 };
+ uint32_t read_args[2] = { address, length };
+ if (sc64_execute_cmd(SC64_CMD_SD_SECTOR_SET, sector_set_args, NULL)) {
+ return true;
+ }
+ return sc64_execute_cmd(SC64_CMD_SD_READ, read_args, NULL);
+}
diff --git a/sw/bootloader/src/sc64.h b/sw/bootloader/src/sc64.h
index 3d24b3b..199c6c2 100644
--- a/sw/bootloader/src/sc64.h
+++ b/sw/bootloader/src/sc64.h
@@ -104,6 +104,8 @@ bool sc64_usb_write_ready (void);
bool sc64_usb_write (uint32_t *address, uint32_t length);
bool sc64_usb_read_ready (uint8_t *type, uint32_t *length);
bool sc64_usb_read (uint32_t *address, uint32_t length);
+bool sc64_sd_card_initialize (void);
+bool sc64_sd_read_sectors (uint32_t starting_sector, uint32_t address, uint32_t length);
#endif
diff --git a/sw/bootloader/src/storage.c b/sw/bootloader/src/storage.c
index 1edd9da..7ed59c7 100644
--- a/sw/bootloader/src/storage.c
+++ b/sw/bootloader/src/storage.c
@@ -61,6 +61,8 @@ void storage_run_menu (storage_backend_t storage_backend) {
FF_CHECK(f_mount(&fs, path, 1), "Couldn't mount drive");
FF_CHECK(f_chdrive(path), "Couldn't chdrive");
FF_CHECK(f_open(&fil, "sc64menu.n64", FA_READ), "Couldn't open menu file");
+ FF_CHECK(f_lseek(&fil, 0), "debug 1");
+ FF_CHECK(f_read(&fil, (void *) (0x10000000UL), f_size(&fil), &br), "debug 2");
FF_CHECK(f_lseek(&fil, ROM_ENTRY_OFFSET), "Couldn't seek to entry point offset");
FF_CHECK(f_read(&fil, &menu, sizeof(menu), &br), "Couldn't read entry point");
FF_CHECK(f_lseek(&fil, ROM_CODE_OFFSET), "Couldn't seek to code start offset");
diff --git a/sw/controller/app.mk b/sw/controller/app.mk
index 5194466..8fd20ee 100644
--- a/sw/controller/app.mk
+++ b/sw/controller/app.mk
@@ -17,6 +17,7 @@ SRC_FILES = \
isv.c \
lcmxo2.c \
rtc.c \
+ sd.c \
task.c \
update.c \
usb.c
diff --git a/sw/controller/src/app.c b/sw/controller/src/app.c
index f2b7020..5625af7 100644
--- a/sw/controller/src/app.c
+++ b/sw/controller/src/app.c
@@ -8,7 +8,7 @@
#define CIC_STACK_SIZE (256)
#define RTC_STACK_SIZE (256)
-#define GVR_STACK_SIZE (1536)
+#define GVR_STACK_SIZE (2048)
uint8_t cic_stack[CIC_STACK_SIZE] __attribute__((aligned(8)));
diff --git a/sw/controller/src/button.c b/sw/controller/src/button.c
index 770b2a9..e2fbaf1 100644
--- a/sw/controller/src/button.c
+++ b/sw/controller/src/button.c
@@ -39,9 +39,9 @@ void button_init (void) {
void button_process (void) {
usb_tx_info_t packet_info;
- uint32_t status = fpga_reg_get(REG_STATUS);
+ uint32_t status = fpga_reg_get(REG_CFG_SCR);
p.shift <<= 1;
- if (status & STATUS_BUTTON) {
+ if (status & CFG_SCR_BUTTON_STATE) {
p.shift |= (1 << 0);
}
if (!p.state && p.shift == 0xFFFFFFFFUL) {
diff --git a/sw/controller/src/cfg.c b/sw/controller/src/cfg.c
index bb1ff5b..b9d0db8 100644
--- a/sw/controller/src/cfg.c
+++ b/sw/controller/src/cfg.c
@@ -5,6 +5,7 @@
#include "fpga.h"
#include "isv.h"
#include "rtc.h"
+#include "sd.h"
#include "usb.h"
@@ -66,6 +67,7 @@ typedef enum {
CFG_ERROR_BAD_ADDRESS = 1,
CFG_ERROR_BAD_CONFIG_ID = 2,
CFG_ERROR_TIMEOUT = 3,
+ CFG_ERROR_SD = 4,
CFG_ERROR_UNKNOWN_CMD = -1,
} cfg_error_t;
@@ -76,6 +78,7 @@ struct process {
cic_seed_t cic_seed;
tv_type_t tv_type;
bool usb_output_ready;
+ uint32_t sd_card_sector;
};
@@ -317,13 +320,16 @@ void cfg_init (void) {
}
void cfg_process (void) {
+ uint32_t reg;
uint32_t args[2];
usb_tx_info_t packet_info;
- if (fpga_reg_get(REG_STATUS) & STATUS_CFG_PENDING) {
+ reg = fpga_reg_get(REG_CFG_CMD);
+
+ if (reg & CFG_CMD_PENDING) {
args[0] = fpga_reg_get(REG_CFG_DATA_0);
args[1] = fpga_reg_get(REG_CFG_DATA_1);
- char cmd = (char) fpga_reg_get(REG_CFG_CMD);
+ char cmd = (char) ((reg & CFG_CMD_MASK) >> CFG_CMD_BIT);
switch (cmd) {
case 'v':
@@ -386,6 +392,26 @@ void cfg_process (void) {
args[0] = p.usb_output_ready ? 1 : 0;
break;
+ case 'i':
+ if (sd_card_initialize()) {
+ cfg_set_error(CFG_ERROR_SD);
+ }
+ break;
+
+ case 'I':
+ p.sd_card_sector = args[0];
+ break;
+
+ case 's':
+ if (cfg_translate_address(args)) {
+ cfg_set_error(CFG_ERROR_BAD_ADDRESS);
+ return;
+ }
+ if (sd_read_sectors(p.sd_card_sector, args[0], args[1])) {
+ cfg_set_error(CFG_ERROR_SD);
+ }
+ break;
+
default:
cfg_set_error(CFG_ERROR_UNKNOWN_CMD);
return;
diff --git a/sw/controller/src/fpga.h b/sw/controller/src/fpga.h
index d67f1ce..6b955bd 100644
--- a/sw/controller/src/fpga.h
+++ b/sw/controller/src/fpga.h
@@ -18,7 +18,6 @@ typedef enum {
} fpga_cmd_t;
typedef enum {
- REG_STATUS,
REG_MEM_ADDRESS,
REG_MEM_SCR,
REG_USB_SCR,
@@ -69,16 +68,16 @@ typedef enum {
#define MEM_SCR_BUSY (1 << 3)
#define MEM_SCR_LENGTH_BIT (4)
-#define STATUS_BUTTON (1 << 0)
-#define STATUS_SD_INSERTED (1 << 1)
-#define STATUS_CFG_PENDING (1 << 2)
-
#define USB_SCR_FIFO_FLUSH (1 << 0)
#define USB_SCR_RXNE (1 << 1)
#define USB_SCR_TXE (1 << 2)
#define USB_SCR_RESET_PENDING (1 << 3)
#define USB_SCR_RESET_ACK (1 << 4)
#define USB_SCR_WRITE_FLUSH (1 << 5)
+#define USB_SCR_RX_COUNT_BIT (6)
+#define USB_SCR_RX_COUNT_MASK (0x7FF << USB_SCR_RX_COUNT_BIT)
+#define USB_SCR_TX_COUNT_BIT (17)
+#define USB_SCR_TX_COUNT_MASK (0x7FF << USB_SCR_TX_COUNT_BIT)
#define DMA_SCR_START (1 << 0)
#define DMA_SCR_STOP (1 << 1)
@@ -97,10 +96,14 @@ typedef enum {
#define CFG_SCR_EEPROM_ENABLED (1 << 9)
#define CFG_SCR_EEPROM_16K (1 << 10)
#define CFG_SCR_ROM_EXTENDED_ENABLED (1 << 11)
+#define CFG_SCR_BUTTON_STATE (1 << 31)
-#define CFG_CMD_DONE (1 << 0)
-#define CFG_CMD_ERROR (1 << 1)
-#define CFG_CMD_IRQ (1 << 2)
+#define CFG_CMD_BIT (0)
+#define CFG_CMD_MASK (0xFF << CFG_CMD_BIT)
+#define CFG_CMD_PENDING (1 << 8)
+#define CFG_CMD_DONE (1 << 9)
+#define CFG_CMD_ERROR (1 << 10)
+#define CFG_CMD_IRQ (1 << 11)
#define FLASHRAM_SCR_DONE (1 << 0)
#define FLASHRAM_SCR_PENDING (1 << 1)
@@ -120,6 +123,30 @@ typedef enum {
#define SD_SCR_CLOCK_MODE_400KHZ (1 << 0)
#define SD_SCR_CLOCK_MODE_25MHZ (2 << 0)
#define SD_SCR_CLOCK_MODE_50MHZ (3 << 0)
+#define SD_SCR_CMD_BUSY (1 << 2)
+#define SD_SCR_CMD_ERROR (1 << 3)
+#define SD_SCR_CARD_BUSY (1 << 4)
+#define SD_SCR_CARD_INSERTED (1 << 5)
+#define SD_SCR_RX_COUNT_BIT (6)
+#define SD_SCR_RX_COUNT_MASK (0x7FF << SD_SCR_RX_COUNT_BIT)
+#define SD_SCR_TX_COUNT_BIT (17)
+#define SD_SCR_TX_COUNT_MASK (0x7FF << SD_SCR_TX_COUNT_BIT)
+
+#define SD_CMD_INDEX_BIT (0)
+#define SD_CMD_INDEX_MASK (0x3F)
+#define SD_CMD_SKIP_RESPONSE (1 << 6)
+#define SD_CMD_RESERVED_RESPONSE (1 << 7)
+#define SD_CMD_LONG_RESPONSE (1 << 8)
+#define SD_CMD_IGNORE_CRC (1 << 9)
+
+#define SD_DAT_FIFO_FLUSH (1 << 0)
+#define SD_DAT_START_WRITE (1 << 1)
+#define SD_DAT_START_READ (1 << 2)
+#define SD_DAT_STOP (1 << 3)
+#define SD_DAT_BLOCKS_BIT (4)
+#define SD_DAT_BLOCKS_MASK (0xFF << SD_DAT_BLOCKS_BIT)
+#define SD_DAT_BUSY (1 << 12)
+#define SD_DAT_ERROR (1 << 13)
#define DD_SCR_HARD_RESET (1 << 0)
#define DD_SCR_HARD_RESET_CLEAR (1 << 1)
diff --git a/sw/controller/src/gvr.c b/sw/controller/src/gvr.c
index e14d99c..bdc836a 100644
--- a/sw/controller/src/gvr.c
+++ b/sw/controller/src/gvr.c
@@ -5,6 +5,7 @@
#include "fpga.h"
#include "isv.h"
#include "rtc.h"
+#include "sd.h"
#include "usb.h"
@@ -16,6 +17,7 @@ void gvr_task (void) {
dd_init();
flashram_init();
isv_init();
+ sd_init();
usb_init();
while (1) {
@@ -25,6 +27,7 @@ void gvr_task (void) {
flashram_process();
isv_process();
rtc_process();
+ sd_process();
usb_process();
}
}
diff --git a/sw/controller/src/sd.c b/sw/controller/src/sd.c
new file mode 100644
index 0000000..0e541db
--- /dev/null
+++ b/sw/controller/src/sd.c
@@ -0,0 +1,285 @@
+#include
+#include
+#include "sd.h"
+#include "fpga.h"
+#include "debug.h"
+#include "hw.h"
+
+
+#define CMD8_ARG_SUPPLY_VOLTAGE_27_36_V (1 << 8)
+#define CMD8_ARG_CHECK_PATTERN (0xAA << 0)
+
+#define ACMD41_ARG_HCS (1 << 30)
+
+#define R3_CCS (1 << 30)
+#define R3_BUSY (1 << 31)
+
+#define R7_SUPPLY_VOLTAGE_27_36_V (1 << 8)
+#define R7_CHECK_PATTERN (0xAA << 0)
+
+
+typedef enum {
+ CLOCK_STOP,
+ CLOCK_400KHZ,
+ CLOCK_25MHZ,
+ CLOCK_50MHZ,
+} sd_clock_t;
+
+typedef enum {
+ RSP_NONE,
+ RSP_R1,
+ RSP_R1b,
+ RSP_R2,
+ RSP_R3,
+ RSP_R6,
+ RSP_R7,
+} rsp_type_t;
+
+
+static bool sd_card_initialized;
+static bool sd_card_type_block;
+static uint32_t sd_rca = 0;
+static volatile bool timeout = false;
+
+
+static void sd_trigger_timeout (void) {
+ timeout = true;
+}
+
+static void sd_set_clock (sd_clock_t mode) {
+ fpga_reg_set(REG_SD_SCR, SD_SCR_CLOCK_MODE_OFF);
+
+ switch (mode) {
+ case CLOCK_400KHZ:
+ fpga_reg_set(REG_SD_SCR, SD_SCR_CLOCK_MODE_400KHZ);
+ break;
+ case CLOCK_25MHZ:
+ fpga_reg_set(REG_SD_SCR, SD_SCR_CLOCK_MODE_25MHZ);
+ break;
+ case CLOCK_50MHZ:
+ fpga_reg_set(REG_SD_SCR, SD_SCR_CLOCK_MODE_50MHZ);
+ break;
+ default:
+ break;
+ }
+}
+
+static bool sd_cmd (uint8_t cmd, uint32_t arg, rsp_type_t rsp_type, void *rsp) {
+ uint32_t scr;
+ uint32_t cmd_data;
+
+ cmd_data = ((cmd << SD_CMD_INDEX_BIT) & SD_CMD_INDEX_MASK);
+ switch (rsp_type) {
+ case RSP_NONE:
+ cmd_data |= SD_CMD_SKIP_RESPONSE;
+ break;
+ case RSP_R2:
+ cmd_data |= (SD_CMD_LONG_RESPONSE | SD_CMD_RESERVED_RESPONSE);
+ break;
+ case RSP_R3:
+ cmd_data |= (SD_CMD_IGNORE_CRC | SD_CMD_RESERVED_RESPONSE);
+ break;
+ default:
+ break;
+ }
+
+ fpga_reg_set(REG_SD_ARG, arg);
+ fpga_reg_set(REG_SD_CMD, cmd_data);
+
+ do {
+ scr = fpga_reg_get(REG_SD_SCR);
+ } while (scr & SD_SCR_CMD_BUSY);
+
+ if (rsp != NULL) {
+ fpga_reg_t rsp_regs[4] = {
+ REG_SD_RSP_3,
+ REG_SD_RSP_2,
+ REG_SD_RSP_1,
+ REG_SD_RSP_0
+ };
+ bool rsp_long = rsp_type & SD_CMD_LONG_RESPONSE;
+ fpga_reg_t rsp_reg = (rsp_long ? 0 : (sizeof(rsp_regs) - 1));
+ uint8_t *rsp_8 = (uint8_t *) (rsp);
+ while (rsp_reg < sizeof(rsp_regs)) {
+ uint32_t rsp_data = fpga_reg_get(rsp_regs[rsp_reg++]);
+ uint8_t *rsp_data_8 = (uint8_t *) (&rsp_data);
+ for (int i = 0; i < 4; i++) {
+ *rsp_8++ = *rsp_data_8++;
+ }
+ };
+ }
+
+ if (rsp_type == RSP_R1b) {
+ do {
+ scr = fpga_reg_get(REG_SD_SCR);
+ } while (scr & SD_SCR_CARD_BUSY);
+ }
+
+ return (scr & SD_SCR_CMD_ERROR);
+}
+
+static bool sd_acmd (uint8_t acmd, uint32_t arg, rsp_type_t rsp_type, void *rsp) {
+ if (sd_cmd(55, sd_rca, RSP_R1, NULL)) {
+ return true;
+ }
+ if (sd_cmd(acmd, arg, rsp_type, rsp)) {
+ return true;
+ }
+ return false;
+}
+
+
+bool sd_read_sectors (uint32_t starting_sector, uint32_t address, uint32_t length) {
+ if (!sd_card_initialized) {
+ return true;
+ }
+
+ if ((length == 0) || (length % 512 != 0) || (length > (256 * 512))) {
+ return true;
+ }
+
+ do {
+ uint32_t blocks = (length / 512);
+
+ timeout = false;
+ hw_tim_setup(TIM_ID_GVR, 5000, sd_trigger_timeout);
+
+ if (!sd_card_type_block) {
+ starting_sector *= 512;
+ }
+
+ // fpga_reg_set(REG_SD_DMA_SCR, DMA_SCR_STOP);
+ // fpga_reg_set(REG_SD_DAT, SD_DAT_STOP | SD_DAT_FIFO_FLUSH);
+
+ fpga_reg_set(REG_SD_DMA_ADDRESS, address);
+ fpga_reg_set(REG_SD_DMA_LENGTH, length);
+ fpga_reg_set(REG_SD_DMA_SCR, DMA_SCR_DIRECTION | DMA_SCR_START);
+
+ fpga_reg_set(REG_SD_DAT, (((blocks - 1) << SD_DAT_BLOCKS_BIT) | SD_DAT_START_READ | SD_DAT_FIFO_FLUSH));
+
+ if (sd_cmd(23, blocks, RSP_R1, NULL)) {
+ break;
+ }
+
+ if (sd_cmd(18, starting_sector, RSP_R1, NULL)) {
+ break;
+ }
+
+ bool error = false;
+
+ while (!timeout) {
+ uint32_t sd_scr = fpga_reg_get(REG_SD_SCR);
+ if (!(sd_scr & SD_SCR_CARD_INSERTED)) {
+ error = true;
+ break;
+ }
+ uint32_t sd_dat = fpga_reg_get(REG_SD_DAT);
+ uint32_t sd_dma_scr = fpga_reg_get(REG_SD_DMA_SCR);
+ if (sd_dat & SD_DAT_ERROR) {
+ error = true;
+ break;
+ }
+ if ((!(sd_dma_scr & DMA_SCR_BUSY)) && (!(sd_dat & SD_DAT_BUSY))) {
+ break;
+ }
+ }
+
+ if (timeout) {
+ break;
+ }
+
+ hw_tim_stop(TIM_ID_GVR);
+
+ if (error) {
+ break;
+ }
+
+ return false;
+ } while (0);
+
+ fpga_reg_set(REG_SD_DMA_SCR, DMA_SCR_STOP);
+ fpga_reg_set(REG_SD_DAT, SD_DAT_STOP | SD_DAT_FIFO_FLUSH);
+
+ return true;
+}
+
+bool sd_card_initialize (void) {
+ bool error;
+ uint32_t arg;
+ uint32_t rsp;
+ bool version_2_or_later = false;
+
+ if (sd_card_initialized) {
+ return false;
+ }
+
+ sd_set_clock(CLOCK_400KHZ);
+
+ do {
+ sd_cmd(0, 0, RSP_NONE, NULL);
+
+ arg = (CMD8_ARG_SUPPLY_VOLTAGE_27_36_V | CMD8_ARG_CHECK_PATTERN);
+ if (!sd_cmd(8, arg, RSP_R7, &rsp)) {
+ version_2_or_later = true;
+ if (rsp != (R7_SUPPLY_VOLTAGE_27_36_V | R7_CHECK_PATTERN)) {
+ break;
+ }
+ }
+
+ arg = ((version_2_or_later ? ACMD41_ARG_HCS : 0) | 0x00FF8000);
+ for (int i = 0; i < 4000; i++) {
+ error = sd_acmd(41, arg, RSP_R3, &rsp);
+ if (error || (rsp & R3_BUSY)) {
+ break;
+ }
+ }
+ if (error || ((rsp & 0x00FF8000) == 0)) {
+ break;
+ }
+ sd_card_type_block = (rsp & R3_CCS);
+
+ if (sd_cmd(2, 0, RSP_R2, NULL)) {
+ break;
+ }
+
+ if (sd_cmd(3, 0, RSP_R6, &rsp)) {
+ break;
+ }
+ sd_rca = rsp & 0xFFFF0000;
+
+ if (sd_cmd(7, sd_rca, RSP_R1b, NULL)) {
+ break;
+ }
+
+ if (sd_acmd(6, 2, RSP_R1, NULL)) {
+ break;
+ }
+
+ sd_set_clock(CLOCK_50MHZ);
+
+ sd_card_initialized = true;
+
+ return false;
+ } while (0);
+
+ sd_rca = 0;
+ sd_cmd(0, 0, RSP_NONE, NULL);
+ sd_set_clock(CLOCK_STOP);
+
+ return true;
+}
+
+void sd_init (void) {
+ sd_card_initialized = false;
+ sd_set_clock(CLOCK_STOP);
+}
+
+void sd_process (void) {
+ if (!(fpga_reg_get(REG_SD_SCR) & SD_SCR_CARD_INSERTED)) {
+ if (sd_card_initialized) {
+ sd_card_initialized = false;
+ sd_rca = 0;
+ sd_set_clock(CLOCK_STOP);
+ }
+ }
+}
diff --git a/sw/controller/src/sd.h b/sw/controller/src/sd.h
new file mode 100644
index 0000000..90afd36
--- /dev/null
+++ b/sw/controller/src/sd.h
@@ -0,0 +1,14 @@
+#ifndef SD_H__
+#define SD_H__
+
+
+#include
+
+
+bool sd_read_sectors (uint32_t starting_sector, uint32_t address, uint32_t length);
+bool sd_card_initialize (void);
+void sd_init (void);
+void sd_process (void);
+
+
+#endif
diff --git a/sw/pc/.gitignore b/sw/pc/.gitignore
index a6ea7e6..baaf232 100644
--- a/sw/pc/.gitignore
+++ b/sw/pc/.gitignore
@@ -1,18 +1,9 @@
**/__pycache__
-/backup
-/roms
-/saves
-*.a
-*.bak
*.bin
-*.dat
-*.data
-*.dll
-*.dylib
*.eep
*.fla
-*.img
*.n64
*.srm
+*.upd
*.v64
*.z64
diff --git a/sw/pc/sc64.py b/sw/pc/sc64.py
index 4c727c2..704fe4a 100755
--- a/sw/pc/sc64.py
+++ b/sw/pc/sc64.py
@@ -433,6 +433,11 @@ class SC64:
address = self.__Address.EEPROM
return self.__read_memory(address, length)
+ def upload_bootloader(self, data: bytes) -> None:
+ if (len(data) > self.__Length.BOOTLOADER):
+ raise ValueError('Bootloader size too big')
+ self.__program_flash(self.__Address.BOOTLOADER, data)
+
def set_rtc(self, t: datetime) -> None:
to_bcd = lambda v: ((int((v / 10) % 10) << 4) | int(int(v) % 10))
data = bytes([
@@ -645,6 +650,7 @@ if __name__ == '__main__':
parser.add_argument('--isv', action='store_true', help='enable IS-Viewer64 support')
parser.add_argument('--debug', action='store_true', help='run debug loop (required for 64DD and IS-Viewer64)')
parser.add_argument('--download-memory', help='download whole memory space and write it to specified file')
+ parser.add_argument('--bootloader', help='bootloader')
if (len(sys.argv) <= 1):
parser.print_help()
@@ -667,6 +673,12 @@ if __name__ == '__main__':
status_callback = lambda status: print(f'{status} ', end='', flush=True)
sc64.update_firmware(f.read(), status_callback)
print('done')
+
+ if (args.bootloader):
+ with open(args.bootloader, 'rb+') as f:
+ print('Uploading Bootloader... ', end='', flush=True)
+ sc64.upload_bootloader(f.read())
+ print('done')
if (args.reset_state):
sc64.reset_state()