mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-29 00:44:13 +01:00
298 lines
8.7 KiB
Verilog
298 lines
8.7 KiB
Verilog
`include "../constants.vh"
|
|
|
|
module sd_interface (
|
|
input i_clk,
|
|
input i_reset,
|
|
|
|
output reg o_sd_clk,
|
|
output reg o_sd_cs,
|
|
output reg o_sd_mosi,
|
|
input i_sd_miso,
|
|
|
|
input i_request,
|
|
input i_write,
|
|
output o_busy,
|
|
output reg o_ack,
|
|
input [7:0] i_address,
|
|
output [31:0] o_data,
|
|
input [31:0] i_data,
|
|
|
|
output o_dma_request,
|
|
output o_dma_write,
|
|
input i_dma_busy,
|
|
input i_dma_ack,
|
|
output reg [3:0] o_dma_bank,
|
|
output reg [23:0] o_dma_address,
|
|
input [31:0] i_dma_data,
|
|
output [31:0] o_dma_data
|
|
);
|
|
|
|
// Register offsets
|
|
|
|
localparam [2:0] REG_SD_SCR = 3'd0;
|
|
localparam [2:0] REG_SD_CS = 3'd1;
|
|
localparam [2:0] REG_SD_DR = 3'd2;
|
|
localparam [2:0] REG_SD_MULTI = 3'd3;
|
|
localparam [2:0] REG_SD_DMA_SCR = 3'd4;
|
|
localparam [2:0] REG_SD_DMA_ADDR = 3'd5;
|
|
|
|
localparam [7:0] MEM_SD_BUFFER_BASE = 8'h80;
|
|
|
|
|
|
// Bus controller
|
|
|
|
reg [8:0] r_o_data;
|
|
|
|
wire w_address_in_buffers = i_address >= MEM_SD_BUFFER_BASE;
|
|
wire [31:0] w_sd_buffer_rx_o_data;
|
|
|
|
assign o_busy = 1'b0;
|
|
assign o_data = w_address_in_buffers ? w_sd_buffer_rx_o_data : {23'd0, r_o_data};
|
|
|
|
always @(posedge i_clk) begin
|
|
o_ack <= !i_reset && i_request && !i_write && !o_busy;
|
|
end
|
|
|
|
|
|
// DMA controller
|
|
|
|
reg r_dma_fifo_flush;
|
|
reg r_dma_fifo_push;
|
|
reg [31:0] r_dma_fifo_data;
|
|
|
|
wire w_dma_fifo_full;
|
|
wire w_dma_fifo_empty;
|
|
|
|
sd_dma sd_dma_inst (
|
|
.i_clk(i_clk),
|
|
.i_reset(i_reset),
|
|
|
|
.i_fifo_flush(r_dma_fifo_flush),
|
|
.i_fifo_push(r_dma_fifo_push),
|
|
.o_fifo_full(w_dma_fifo_full),
|
|
.o_fifo_empty(w_dma_fifo_empty),
|
|
.i_fifo_data(r_dma_fifo_data),
|
|
|
|
.o_request(o_dma_request),
|
|
.o_write(o_dma_write),
|
|
.i_busy(i_dma_busy),
|
|
.o_data(o_dma_data)
|
|
);
|
|
|
|
|
|
// Bus <-> peripheral interface registers
|
|
|
|
reg r_spi_busy;
|
|
|
|
reg [2:0] r_spi_clk_div;
|
|
reg r_spi_start;
|
|
reg [7:0] r_spi_tx_data;
|
|
reg [7:0] r_spi_rx_data;
|
|
|
|
reg r_spi_rx_only;
|
|
|
|
reg r_spi_start_multi;
|
|
reg [8:0] r_spi_multi_length;
|
|
reg r_spi_multi_dma;
|
|
|
|
|
|
// Write logic
|
|
|
|
wire w_dma_request_successful = o_dma_request && !i_dma_busy;
|
|
|
|
always @(posedge i_clk) begin
|
|
r_spi_start <= 1'b0;
|
|
r_spi_start_multi <= 1'b0;
|
|
r_dma_fifo_flush <= 1'b0;
|
|
|
|
if (i_reset) begin
|
|
o_sd_cs <= 1'b1;
|
|
o_dma_bank <= 4'd0;
|
|
o_dma_address <= 24'd0;
|
|
r_spi_clk_div <= 3'b111;
|
|
end else if (i_request && i_write && !o_busy && !w_address_in_buffers) begin
|
|
case (i_address[2:0])
|
|
REG_SD_SCR: begin
|
|
r_spi_clk_div <= i_data[3:1];
|
|
end
|
|
REG_SD_CS: if (!r_spi_busy) begin
|
|
o_sd_cs <= i_data[0];
|
|
end
|
|
REG_SD_DR: if (!r_spi_busy) begin
|
|
r_spi_start <= 1'b1;
|
|
r_spi_tx_data <= i_data[7:0];
|
|
r_spi_rx_only <= 1'b0;
|
|
r_spi_multi_length <= 9'd0;
|
|
r_spi_multi_dma <= 1'b0;
|
|
end
|
|
REG_SD_MULTI: if (!r_spi_busy) begin
|
|
r_spi_start_multi <= 1'b1;
|
|
{r_spi_multi_dma, r_spi_rx_only, r_spi_multi_length} <= i_data[10:0];
|
|
end
|
|
REG_SD_DMA_SCR: begin
|
|
{r_dma_fifo_flush} <= i_data[0];
|
|
end
|
|
REG_SD_DMA_ADDR: begin
|
|
{o_dma_bank, o_dma_address} <= {i_data[31:28], i_data[25:2]};
|
|
end
|
|
default: begin
|
|
end
|
|
endcase
|
|
end
|
|
|
|
if (w_dma_request_successful) begin
|
|
o_dma_address <= o_dma_address + 1'd1;
|
|
end
|
|
end
|
|
|
|
|
|
// Read logic
|
|
|
|
always @(posedge i_clk) begin
|
|
if (!i_reset && i_request && !i_write && !o_busy) begin
|
|
if (!w_address_in_buffers) begin
|
|
case (i_address[2:0])
|
|
REG_SD_SCR: begin
|
|
r_o_data[3:0] <= {r_spi_clk_div, r_spi_busy};
|
|
end
|
|
REG_SD_DR: begin
|
|
r_o_data[8:0] <= {r_spi_busy, r_spi_rx_data};
|
|
end
|
|
REG_SD_DMA_SCR: begin
|
|
r_o_data[1:0] <= {w_dma_fifo_full, w_dma_fifo_empty};
|
|
end
|
|
default: begin
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
// Clock divider
|
|
|
|
reg [7:0] r_spi_clk_div_counter;
|
|
reg r_spi_clk_prev_value;
|
|
reg r_spi_clk_strobe;
|
|
|
|
wire w_spi_clk_div_selected = r_spi_clk_div_counter[r_spi_clk_div];
|
|
|
|
always @(posedge i_clk) begin
|
|
r_spi_clk_div_counter <= r_spi_clk_div_counter + 1'd1;
|
|
r_spi_clk_prev_value <= w_spi_clk_div_selected;
|
|
r_spi_clk_strobe <= (w_spi_clk_div_selected != r_spi_clk_prev_value) && !w_dma_fifo_full;
|
|
end
|
|
|
|
|
|
// Buffers
|
|
|
|
reg [8:0] r_spi_multi_tx_address;
|
|
wire [7:0] w_spi_multi_tx_data;
|
|
|
|
ram_sd_buffer ram_sd_buffer_tx_inst (
|
|
.clock(i_clk),
|
|
|
|
.address_a(r_spi_multi_tx_address),
|
|
.q_a(w_spi_multi_tx_data),
|
|
|
|
.wren_b(!i_reset && i_request && i_write && !o_busy && w_address_in_buffers),
|
|
.address_b(i_address[6:0]),
|
|
.data_b({i_data[7:0], i_data[15:8], i_data[23:16], i_data[31:24]})
|
|
);
|
|
|
|
reg r_spi_multi_rx_byte_write;
|
|
reg [8:0] r_spi_multi_rx_address;
|
|
|
|
ram_sd_buffer ram_sd_buffer_rx_inst (
|
|
.clock(i_clk),
|
|
|
|
.wren_a(r_spi_multi_rx_byte_write),
|
|
.address_a(r_spi_multi_rx_address),
|
|
.data_a(r_spi_rx_data),
|
|
|
|
.address_b(i_address[6:0]),
|
|
.q_b({w_sd_buffer_rx_o_data[7:0], w_sd_buffer_rx_o_data[15:8], w_sd_buffer_rx_o_data[23:16], w_sd_buffer_rx_o_data[31:24]})
|
|
);
|
|
|
|
|
|
// Shifting operation
|
|
|
|
reg r_spi_multi;
|
|
reg r_spi_first_bit;
|
|
reg [3:0] r_spi_bit_clk;
|
|
reg [6:0] r_spi_tx_shift;
|
|
|
|
wire [7:0] w_next_spi_tx_data = r_spi_rx_only ? 8'hFF : (r_spi_multi ? w_spi_multi_tx_data : r_spi_tx_data);
|
|
|
|
always @(posedge i_clk) begin
|
|
r_spi_multi_rx_byte_write <= 1'b0;
|
|
|
|
if (i_reset) begin
|
|
o_sd_clk <= 1'b0;
|
|
r_spi_bit_clk <= 4'd8;
|
|
r_spi_multi_tx_address <= 9'd0;
|
|
r_spi_multi_rx_address <= 9'd0;
|
|
end else begin
|
|
if (!r_spi_busy && (r_spi_start || r_spi_start_multi)) begin
|
|
r_spi_busy <= 1'b1;
|
|
r_spi_multi <= r_spi_start_multi;
|
|
r_spi_first_bit <= 1'b1;
|
|
end else if (r_spi_busy && r_spi_clk_strobe) begin
|
|
if (r_spi_first_bit) begin
|
|
r_spi_first_bit <= 1'b0;
|
|
{o_sd_mosi, r_spi_tx_shift} <= w_next_spi_tx_data;
|
|
r_spi_multi_tx_address <= r_spi_multi_tx_address + 1'd1;
|
|
end else if (!o_sd_clk) begin
|
|
o_sd_clk <= 1'b1;
|
|
r_spi_rx_data <= {r_spi_rx_data[6:0], i_sd_miso};
|
|
r_spi_bit_clk <= r_spi_bit_clk - 1'd1;
|
|
if (r_spi_bit_clk == 4'd1) begin
|
|
r_spi_multi_rx_byte_write <= 1'b1;
|
|
end
|
|
end else begin
|
|
o_sd_clk <= 1'b0;
|
|
if (r_spi_bit_clk == 4'd0) begin
|
|
{o_sd_mosi, r_spi_tx_shift} <= w_next_spi_tx_data;
|
|
r_spi_bit_clk <= 4'd8;
|
|
r_spi_multi_tx_address <= r_spi_multi_tx_address + 1'd1;
|
|
r_spi_multi_rx_address <= r_spi_multi_rx_address + 1'd1;
|
|
if (r_spi_multi_rx_address == r_spi_multi_length) begin
|
|
r_spi_busy <= 1'b0;
|
|
r_spi_multi_tx_address <= 9'd0;
|
|
r_spi_multi_rx_address <= 9'd0;
|
|
end
|
|
end else begin
|
|
{o_sd_mosi, r_spi_tx_shift} <= {r_spi_tx_shift, 1'b0};
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
// DMA RX FIFO controller
|
|
|
|
reg [1:0] r_dma_byte_counter;
|
|
|
|
always @(posedge i_clk) begin
|
|
r_dma_fifo_push <= 1'b0;
|
|
|
|
if (i_reset) begin
|
|
r_dma_byte_counter <= 2'd0;
|
|
end else begin
|
|
if (!r_spi_busy || (r_spi_busy && !r_spi_multi_dma)) begin
|
|
if (r_dma_fifo_flush) begin
|
|
r_dma_byte_counter <= 2'd0;
|
|
end
|
|
end else if (r_spi_multi_rx_byte_write && r_spi_multi_dma) begin
|
|
r_dma_byte_counter <= r_dma_byte_counter + 1'd1;
|
|
r_dma_fifo_data <= {r_dma_fifo_data[23:0], r_spi_rx_data};
|
|
if (r_dma_byte_counter == 2'd3) begin
|
|
r_dma_fifo_push <= 1'b1;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
endmodule
|