277 lines
10 KiB
Systemverilog
277 lines
10 KiB
Systemverilog
/* SD Card controller module. Allows reading from and writing to a microSD card
|
|
through SPI mode. */
|
|
|
|
`timescale 1ns / 1ps
|
|
|
|
module sd_controller(
|
|
output reg cs, // Connect to SD_DAT[3].
|
|
output mosi, // Connect to SD_CMD.
|
|
input miso, // Connect to SD_DAT[0].
|
|
output sclk, // Connect to SD_SCK.
|
|
// For SPI mode, SD_DAT[2] and SD_DAT[1] should be held HIGH.
|
|
// SD_RESET should be held LOW.
|
|
|
|
input rd, // Read-enable. When [ready] is HIGH, asseting [rd] will
|
|
// begin a 512-byte READ operation at [address].
|
|
// [byte_available] will transition HIGH as a new byte has been
|
|
// read from the SD card. The byte is presented on [dout].
|
|
output reg [7:0] dout, // Data output for READ operation.
|
|
output reg byte_available, // A new byte has been presented on [dout].
|
|
|
|
input wr, // Write-enable. When [ready] is HIGH, asserting [wr] will
|
|
// begin a 512-byte WRITE operation at [address].
|
|
// [ready_for_next_byte] will transition HIGH to request that
|
|
// the next byte to be written should be presentaed on [din].
|
|
input [7:0] din, // Data input for WRITE operation.
|
|
output reg ready_for_next_byte, // A new byte should be presented on [din].
|
|
|
|
input reset, // Resets controller on assertion.
|
|
output ready, // HIGH if the SD card is ready for a read or write operation.
|
|
input [31:0] address, // Memory address for read/write operation. This MUST
|
|
// be a multiple of 512 bytes, due to SD sectoring.
|
|
input clk, // 25 MHz clock.
|
|
output [4:0] status // For debug purposes: Current state of controller.
|
|
);
|
|
|
|
parameter RST = 0;
|
|
parameter INIT = 1;
|
|
parameter CMD0 = 2;
|
|
parameter CMD55 = 3;
|
|
parameter CMD41 = 4;
|
|
parameter POLL_CMD = 5;
|
|
|
|
parameter IDLE = 6;
|
|
parameter READ_BLOCK = 7;
|
|
parameter READ_BLOCK_WAIT = 8;
|
|
parameter READ_BLOCK_DATA = 9;
|
|
parameter READ_BLOCK_CRC = 10;
|
|
parameter SEND_CMD = 11;
|
|
parameter RECEIVE_BYTE_WAIT = 12;
|
|
parameter RECEIVE_BYTE = 13;
|
|
parameter WRITE_BLOCK_CMD = 14;
|
|
parameter WRITE_BLOCK_INIT = 15;
|
|
parameter WRITE_BLOCK_DATA = 16;
|
|
parameter WRITE_BLOCK_BYTE = 17;
|
|
parameter WRITE_BLOCK_WAIT = 18;
|
|
|
|
parameter WRITE_DATA_SIZE = 515;
|
|
|
|
reg [4:0] state = RST;
|
|
assign status = state;
|
|
reg [4:0] return_state;
|
|
reg sclk_sig = 0;
|
|
reg [55:0] cmd_out;
|
|
reg [7:0] recv_data;
|
|
reg cmd_mode = 1;
|
|
reg [7:0] data_sig = 8'hFF;
|
|
|
|
reg [9:0] byte_counter;
|
|
reg [9:0] bit_counter;
|
|
|
|
reg [26:0] boot_counter = 27'd100_000_000;
|
|
always @(posedge clk) begin
|
|
if(reset == 1) begin
|
|
state <= RST;
|
|
sclk_sig <= 0;
|
|
boot_counter <= 27'd100_000_000;
|
|
end
|
|
else begin
|
|
case(state)
|
|
RST: begin
|
|
if(boot_counter == 0) begin
|
|
sclk_sig <= 0;
|
|
cmd_out <= {56{1'b1}};
|
|
byte_counter <= 0;
|
|
byte_available <= 0;
|
|
ready_for_next_byte <= 0;
|
|
cmd_mode <= 1;
|
|
bit_counter <= 160;
|
|
cs <= 1;
|
|
state <= INIT;
|
|
end
|
|
else begin
|
|
boot_counter <= boot_counter - 1;
|
|
end
|
|
end
|
|
INIT: begin
|
|
if(bit_counter == 0) begin
|
|
cs <= 0;
|
|
state <= CMD0;
|
|
end
|
|
else begin
|
|
bit_counter <= bit_counter - 1;
|
|
sclk_sig <= ~sclk_sig;
|
|
end
|
|
end
|
|
CMD0: begin
|
|
cmd_out <= 56'hFF_40_00_00_00_00_95;
|
|
bit_counter <= 55;
|
|
return_state <= CMD55;
|
|
state <= SEND_CMD;
|
|
end
|
|
CMD55: begin
|
|
cmd_out <= 56'hFF_77_00_00_00_00_01;
|
|
bit_counter <= 55;
|
|
return_state <= CMD41;
|
|
state <= SEND_CMD;
|
|
end
|
|
CMD41: begin
|
|
cmd_out <= 56'hFF_69_00_00_00_00_01;
|
|
bit_counter <= 55;
|
|
return_state <= POLL_CMD;
|
|
state <= SEND_CMD;
|
|
end
|
|
POLL_CMD: begin
|
|
if(recv_data[0] == 0) begin
|
|
state <= IDLE;
|
|
end
|
|
else begin
|
|
state <= CMD55;
|
|
end
|
|
end
|
|
IDLE: begin
|
|
if(rd == 1) begin
|
|
state <= READ_BLOCK;
|
|
end
|
|
else if(wr == 1) begin
|
|
state <= WRITE_BLOCK_CMD;
|
|
end
|
|
else begin
|
|
state <= IDLE;
|
|
end
|
|
end
|
|
READ_BLOCK: begin
|
|
cmd_out <= {16'hFF_51, address, 8'hFF};
|
|
bit_counter <= 55;
|
|
return_state <= READ_BLOCK_WAIT;
|
|
state <= SEND_CMD;
|
|
end
|
|
READ_BLOCK_WAIT: begin
|
|
if(sclk_sig == 1 && miso == 0) begin
|
|
byte_counter <= 511;
|
|
bit_counter <= 7;
|
|
return_state <= READ_BLOCK_DATA;
|
|
state <= RECEIVE_BYTE;
|
|
end
|
|
sclk_sig <= ~sclk_sig;
|
|
end
|
|
READ_BLOCK_DATA: begin
|
|
dout <= recv_data;
|
|
byte_available <= 1;
|
|
if (byte_counter == 0) begin
|
|
bit_counter <= 7;
|
|
return_state <= READ_BLOCK_CRC;
|
|
state <= RECEIVE_BYTE;
|
|
end
|
|
else begin
|
|
byte_counter <= byte_counter - 1;
|
|
return_state <= READ_BLOCK_DATA;
|
|
bit_counter <= 7;
|
|
state <= RECEIVE_BYTE;
|
|
end
|
|
end
|
|
READ_BLOCK_CRC: begin
|
|
bit_counter <= 7;
|
|
return_state <= IDLE;
|
|
state <= RECEIVE_BYTE;
|
|
end
|
|
SEND_CMD: begin
|
|
if (sclk_sig == 1) begin
|
|
if (bit_counter == 0) begin
|
|
state <= RECEIVE_BYTE_WAIT;
|
|
end
|
|
else begin
|
|
bit_counter <= bit_counter - 1;
|
|
cmd_out <= {cmd_out[54:0], 1'b1};
|
|
end
|
|
end
|
|
sclk_sig <= ~sclk_sig;
|
|
end
|
|
RECEIVE_BYTE_WAIT: begin
|
|
if (sclk_sig == 1) begin
|
|
if (miso == 0) begin
|
|
recv_data <= 0;
|
|
bit_counter <= 6;
|
|
state <= RECEIVE_BYTE;
|
|
end
|
|
end
|
|
sclk_sig <= ~sclk_sig;
|
|
end
|
|
RECEIVE_BYTE: begin
|
|
byte_available <= 0;
|
|
if (sclk_sig == 1) begin
|
|
recv_data <= {recv_data[6:0], miso};
|
|
if (bit_counter == 0) begin
|
|
state <= return_state;
|
|
end
|
|
else begin
|
|
bit_counter <= bit_counter - 1;
|
|
end
|
|
end
|
|
sclk_sig <= ~sclk_sig;
|
|
end
|
|
WRITE_BLOCK_CMD: begin
|
|
cmd_out <= {16'hFF_58, address, 8'hFF};
|
|
bit_counter <= 55;
|
|
return_state <= WRITE_BLOCK_INIT;
|
|
state <= SEND_CMD;
|
|
ready_for_next_byte <= 1;
|
|
end
|
|
WRITE_BLOCK_INIT: begin
|
|
cmd_mode <= 0;
|
|
byte_counter <= WRITE_DATA_SIZE;
|
|
state <= WRITE_BLOCK_DATA;
|
|
ready_for_next_byte <= 0;
|
|
end
|
|
WRITE_BLOCK_DATA: begin
|
|
if (byte_counter == 0) begin
|
|
state <= RECEIVE_BYTE_WAIT;
|
|
return_state <= WRITE_BLOCK_WAIT;
|
|
end
|
|
else begin
|
|
if ((byte_counter == 2) || (byte_counter == 1)) begin
|
|
data_sig <= 8'hFF;
|
|
end
|
|
else if (byte_counter == WRITE_DATA_SIZE) begin
|
|
data_sig <= 8'hFE;
|
|
end
|
|
else begin
|
|
data_sig <= din;
|
|
ready_for_next_byte <= 1;
|
|
end
|
|
bit_counter <= 7;
|
|
state <= WRITE_BLOCK_BYTE;
|
|
byte_counter <= byte_counter - 1;
|
|
end
|
|
end
|
|
WRITE_BLOCK_BYTE: begin
|
|
if (sclk_sig == 1) begin
|
|
if (bit_counter == 0) begin
|
|
state <= WRITE_BLOCK_DATA;
|
|
ready_for_next_byte <= 0;
|
|
end
|
|
else begin
|
|
data_sig <= {data_sig[6:0], 1'b1};
|
|
bit_counter <= bit_counter - 1;
|
|
end;
|
|
end;
|
|
sclk_sig <= ~sclk_sig;
|
|
end
|
|
WRITE_BLOCK_WAIT: begin
|
|
if (sclk_sig == 1) begin
|
|
if (miso == 1) begin
|
|
state <= IDLE;
|
|
cmd_mode <= 1;
|
|
end
|
|
end
|
|
sclk_sig = ~sclk_sig;
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
assign sclk = sclk_sig;
|
|
assign mosi = cmd_mode ? cmd_out[55] : data_sig[7];
|
|
assign ready = (state == IDLE);
|
|
endmodule
|