OpenCores
URL https://opencores.org/ocsvn/ao486/ao486/trunk

Subversion Repositories ao486

[/] [ao486/] [trunk/] [rtl/] [soc/] [driver_sd/] [driver_sd.v] - Rev 2

Go to most recent revision | Compare with Previous | Blame | View Log

/*
 * Copyright (c) 2014, Aleksander Osman
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
module driver_sd(
 
    input           clk,
    input           rst_n,
 
    input       [1:0]   avs_address,
    output              avs_waitrequest,
    input               avs_read,
    output      [31:0]  avs_readdata,
    input               avs_write,
    input       [31:0]  avs_writedata,
 
    output reg  [31:0]  avm_address,
    input               avm_waitrequest,
    output reg          avm_read,
    input       [31:0]  avm_readdata,
    input               avm_readdatavalid,
    output reg          avm_write,
    output reg  [31:0]  avm_writedata,
 
    output reg      sd_clk,
    inout           sd_cmd,
    inout   [3:0]   sd_dat
);
 
//------------------------------------------------------------------------------
 
//------------------------------------------------------------------------------ Avalon master
 
reg data_read;
reg data_write;
reg [31:0] data_part_contents;
reg wait_for_readdatavalid;
 
always @(posedge clk or negedge rst_n) begin
	if(rst_n == 1'b0) begin
	    avm_address             <= 32'd0;
		avm_read                <= 1'b0;
        avm_write               <= 1'b0;
        avm_writedata           <= 32'b0;
 
		data_read               <= 1'b0;
		data_write              <= 1'b0;
		data_part_contents      <= 32'd0;
        wait_for_readdatavalid  <= 1'b0;
	end
	else if(data_state == S_DATA_READ_READY_PART && data_read == 1'b0) begin
		if(avm_write && avm_waitrequest == 1'b0) begin
			avm_write <= 1'b0;
 
			data_read <= 1'b1;
		end
		else begin
		    avm_address   <= avalon_address_base + { 23'b0, part_counter, 2'b0 };
            avm_write     <= 1'b1;
            avm_writedata <= { data_part[7:0], data_part[15:8], data_part[23:16], data_part[31:24] };
		end
	end
	else if(data_state == S_DATA_WRITE_READY_PART && data_write == 1'b0) begin
		if(avm_read && avm_waitrequest == 1'b0) begin
            avm_read <= 1'b0;
        end
        else if(wait_for_readdatavalid && avm_readdatavalid) begin
            avm_read <= 1'b0;
            wait_for_readdatavalid <= 1'b0;
			data_part_contents <= { avm_readdata[7:0], avm_readdata[15:8], avm_readdata[23:16], avm_readdata[31:24] };
 
			data_write <= 1'b1;
		end
		else begin
		    avm_address             <= avalon_address_base + { 23'b0, part_counter, 2'b0 };
			avm_read                <= 1'b1;
            wait_for_readdatavalid  <= 1'b1;
		end
	end
	else if(data_state != S_DATA_READ_READY_PART && data_state != S_DATA_WRITE_READY_PART) begin
		data_read <= 1'b0;
		data_write <= 1'b0;
	end
end
 
//------------------------------------------------------------------------------ Avalon slave
 
//read mutex
 
assign avs_readdata = (avs_address == 2'd0)? {29'd0, status[2:0]} : { 29'd0, mutex };
 
reg [2:0] mutex;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                                               mutex <= 3'd0;
    else if(mutex == 3'd0 && avs_address == 2'd1 && avs_read && ~(avs_waitrequest)) mutex <= 3'd1;
    else if(mutex == 3'd0 && avs_address == 2'd2 && avs_read && ~(avs_waitrequest)) mutex <= 2'd2;
    else if(mutex == 3'd0 && avs_address == 2'd3 && avs_read && ~(avs_waitrequest)) mutex <= 3'd3;
    else if(mutex < 3'd4 && (status == STATUS_READ || status == STATUS_WRITE))      mutex <= 3'd4;
    else if(status == STATUS_IDLE && status_last != STATUS_IDLE)                    mutex <= 3'd0;
end
 
assign avs_waitrequest = control_state == S_CTRL_PRE_IDLE;
 
// write only
reg [31:0] sd_address;
reg [31:0] sd_block_count;
reg [1:0]  control;
reg [31:0] avalon_address_base;
 
localparam [1:0] CONTROL_IDLE   = 2'd0;
localparam [1:0] CONTROL_REINIT = 2'd1;
localparam [1:0] CONTROL_READ	= 2'd2;
localparam [1:0] CONTROL_WRITE	= 2'd3;
 
always @(posedge clk or negedge rst_n) begin
	if(rst_n == 1'b0) begin
		avalon_address_base <= 32'd0;
		sd_address          <= 32'd0;
		sd_block_count      <= 32'd0;
		control             <= 2'd0;
	end
	else begin
		if(control_state == S_CTRL_PRE_IDLE) begin
			sd_block_count      <= sd_block_count - 32'd1;
			sd_address          <= sd_address + 32'd1;
			avalon_address_base <= avalon_address_base + 32'd512;
 
			if(sd_block_count == 32'd1) control <= CONTROL_IDLE;
		end
		else if(avs_write) begin
	        if(avs_address == 2'd0)         avalon_address_base <= avs_writedata;
	        else if(avs_address == 2'd1)    sd_address          <= avs_writedata;
	        else if(avs_address == 2'd2)    sd_block_count      <= avs_writedata;
	        else if(avs_address == 2'd3)    control             <= avs_writedata[1:0];
        end
	end
 
end
 
//------------------------------------------------------------------------------ Control state machine
 
reg [3:0]  control_state;
reg [15:0] error_count;
reg [2:0]  status;
reg [37:0] cmd_send_contents;
 
reg start_cmd;
reg start_read;
reg start_write;
 
reg [2:0] status_last;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)   status_last <= STATUS_INIT;
    else                status_last <= status;
end
 
`define CRC7_REVERSE crc7[0],crc7[1],crc7[2],crc7[3],crc7[4],crc7[5],crc7[6]
 
localparam [3:0] S_CTRL_INIT        = 4'd0;
localparam [3:0] S_CTRL_CMD0        = 4'd1;
localparam [3:0] S_CTRL_CMD8        = 4'd2;
localparam [3:0] S_CTRL_CMD55       = 4'd3;
localparam [3:0] S_CTRL_ACMD41      = 4'd4;
localparam [3:0] S_CTRL_CMD2        = 4'd5;
localparam [3:0] S_CTRL_CMD3        = 4'd6;
localparam [3:0] S_CTRL_CMD7        = 4'd7;
localparam [3:0] S_CTRL_PRE_IDLE    = 4'd8;
localparam [3:0] S_CTRL_IDLE        = 4'd9;
localparam [3:0] S_CTRL_CMD17_READ  = 4'd10;
localparam [3:0] S_CTRL_CMD24_WRITE = 4'd11;
 
localparam [2:0] STATUS_INIT        = 3'd0;
localparam [2:0] STATUS_INIT_ERROR  = 3'd1;
localparam [2:0] STATUS_IDLE        = 3'd2;
localparam [2:0] STATUS_READ        = 3'd3;
localparam [2:0] STATUS_WRITE       = 3'd4;
localparam [2:0] STATUS_ERROR       = 3'd5;
 
always @(posedge clk or negedge rst_n) begin
	if(rst_n == 1'b0) begin
		control_state <= S_CTRL_INIT;
		status <= STATUS_INIT;
		cmd_send_contents <= 38'd0;
		start_cmd <= 1'b0;
		start_read <= 1'b0;
		start_write <= 1'b0;
		error_count <= 16'd0;
	end
	else if(control_state == S_CTRL_INIT && error_count == 16'd65535) begin
		status <= STATUS_INIT_ERROR;
 
		if(control == CONTROL_REINIT) begin
			error_count <= 16'd0;
			control_state <= S_CTRL_INIT;
		end
	end
	else if(control_state == S_CTRL_INIT) begin
		status <= STATUS_INIT;
 
		if(cmd_state == S_CMD_IDLE && start_cmd == 1'b0) begin
		    start_cmd <= 1'b1;
			//CMD0, no arguments
			cmd_send_contents <= { 6'd0, 32'd0 };
			control_state <= S_CTRL_CMD0;
		end
	end
	else if(control_state == S_CTRL_CMD0) begin
 
		if(cmd_state == S_CMD_REPLY_ERROR) begin
			error_count <= error_count + 16'd1;
			control_state <= S_CTRL_INIT;
		end
		else if(cmd_state == S_CMD_IDLE && start_cmd == 1'b0) begin
			start_cmd <= 1'b1;
			//CMD8, supply voltage, check pattern
			cmd_send_contents <= { 6'd8, 20'd0, 4'b0001, 8'b10101010 };
			control_state <= S_CTRL_CMD8;
		end
		else start_cmd <= 1'b0;
	end
	else if(control_state == S_CTRL_CMD8) begin
		if(start_cmd == 1'b1) begin
			start_cmd <= 1'b0;
		end
		else if(cmd_state == S_CMD_REPLY_ERROR ||
			(cmd_state == S_CMD_IDLE && cmd_reply != { 1'b0, 1'b0, 6'd8, 20'd0, 4'b0001, 8'b10101010, `CRC7_REVERSE, 1'b1 })
		) begin
			error_count <= error_count + 16'd1;
			control_state <= S_CTRL_INIT;
		end
		else if(cmd_state == S_CMD_IDLE) begin
			start_cmd <= 1'b1;
			//CMD55, RCA
			cmd_send_contents <= { 6'd55, 16'd0, 16'd0};
			control_state <= S_CTRL_CMD55;
		end
	end
	else if(control_state == S_CTRL_CMD55) begin
		if(start_cmd == 1'b1) begin
			start_cmd <= 1'b0;
		end
		else if(cmd_state == S_CMD_REPLY_ERROR ||
			(cmd_state == S_CMD_IDLE &&
				(cmd_reply[47:40] != { 1'b0, 1'b0, 6'd55 } || cmd_reply[39:27] != 13'b0 || cmd_reply[24:21] != 4'b0 ||
				 cmd_reply[13] != 1'b1 || cmd_reply[11] != 1'b0 || cmd_reply[7:0] != { `CRC7_REVERSE, 1'b1 }
				)
			)
		) begin
			error_count <= error_count + 16'd1;
			control_state <= S_CTRL_INIT;
		end
		else if(cmd_state == S_CMD_IDLE && start_cmd == 1'b0) begin
			start_cmd <= 1'b1;
			//ACMD41, 
			cmd_send_contents <= { 	6'd41, 								//command index
									1'b0, 								//reserved bit
									1'b1,								//host capacity support HCS(OCR[30])
									6'b0,								//reserved bits
									24'b0001_0000_0000_0000_0000_0000	//VDD voltage window OCR[23:0]
			};
			control_state <= S_CTRL_ACMD41;
		end
	end
	else if(control_state == S_CTRL_ACMD41) begin
		if(start_cmd == 1'b1) begin
			start_cmd <= 1'b0;
		end
		else if(cmd_state == S_CMD_REPLY_ERROR ||
			(cmd_state == S_CMD_IDLE && (cmd_reply[47:40] != { 1'b0, 1'b0, 6'b111111 } || 
				cmd_reply[39:38] != 2'b11 || cmd_reply[7:0] != {7'b1111111, 1'b1 })
			)
		) begin
			if(error_count == 16'd65535) begin
				control_state <= S_CTRL_INIT;
			end
			else begin
				error_count <= error_count + 16'd1;
				start_cmd <= 1'b1;
				//CMD55, RCA
				cmd_send_contents <= { 6'd55, 16'd0, 16'd0};
				control_state <= S_CTRL_CMD55;
			end
		end
		else if(cmd_state == S_CMD_IDLE && start_cmd == 1'b0) begin
			start_cmd <= 1'b1;
			//CMD2, no arguments
			cmd_send_contents <= { 6'd2, 32'd0 };
			control_state <= S_CTRL_CMD2;
		end
	end
	else if(control_state == S_CTRL_CMD2) begin
		if(start_cmd == 1'b1) begin
			start_cmd <= 1'b0;
		end
		else if(cmd_state == S_CMD_REPLY_ERROR ||
			(cmd_state == S_CMD_IDLE && cmd_reply[0] != 1'b1)
		) begin
			error_count <= error_count + 16'd1;
			control_state <= S_CTRL_INIT;
		end
		else if(cmd_state == S_CMD_IDLE && start_cmd == 1'b0) begin
			start_cmd <= 1'b1;
			//CMD3, no arguments
			cmd_send_contents <= { 6'd3, 32'd0 };
			control_state <= S_CTRL_CMD3;
		end
	end
	else if(control_state == S_CTRL_CMD3) begin
		if(start_cmd == 1'b1) begin
			start_cmd <= 1'b0;
		end
		else if(cmd_state == S_CMD_REPLY_ERROR ||
			(cmd_state == S_CMD_IDLE &&
				(cmd_reply[47:40] != { 1'b0, 1'b0, 6'd3 } || 
				 /*23:8= 23,22,19,12:0 from card status*/
				 cmd_reply[23:21] != 3'b0 || cmd_reply[13] != 1'b0 || cmd_reply[11] != 1'b0 || 
				 cmd_reply[7:0] != { `CRC7_REVERSE, 1'b1 }
				)
			)
		) begin
			error_count <= error_count + 16'd1;
			control_state <= S_CTRL_INIT;
		end
		else if(cmd_state == S_CMD_IDLE && start_cmd == 1'b0) begin
 
			start_cmd <= 1'b1;
			//CMD7, no arguments
			cmd_send_contents <= { 	6'd7,				//command index
									cmd_reply[39:24], 	//RCA
									16'd0				//stuff bits
			};
			control_state <= S_CTRL_CMD7;
		end
	end
	else if(control_state == S_CTRL_CMD7) begin
		if(start_cmd == 1'b1) begin
			start_cmd <= 1'b0;
		end
		else if(cmd_state == S_CMD_REPLY_ERROR ||
			(cmd_state == S_CMD_IDLE &&
				(cmd_reply[47:40] != { 1'b0, 1'b0, 6'd7 } || cmd_reply[39:27] != 13'b0 || cmd_reply[24:21] != 4'b0 ||
				 cmd_reply[13] != 1'b0 || cmd_reply[11] != 1'b0 || cmd_reply[7:0] != { `CRC7_REVERSE, 1'b1 }
				)
			)
		) begin
			error_count <= error_count + 16'd1;
			control_state <= S_CTRL_INIT;
		end
		else if(cmd_state == S_CMD_IDLE && start_cmd == 1'b0) begin
			start_cmd <= 1'b0;
 
			error_count <= 16'd0;
			control_state <= S_CTRL_IDLE;
		end
	end
	else if(control_state == S_CTRL_PRE_IDLE) begin
		control_state <= S_CTRL_IDLE;
	end
	else if(control_state == S_CTRL_IDLE && error_count != 16'd0) begin
		status <= STATUS_ERROR;
 
		if(control == CONTROL_IDLE) begin
			control_state <= S_CTRL_IDLE;
			error_count <= 16'd0;
		end
		else if(control == CONTROL_REINIT) begin
			control_state <= S_CTRL_INIT;
			error_count <= 16'd0;
		end
	end
	else if(control_state == S_CTRL_IDLE) begin
		if(control == CONTROL_READ && sd_block_count != 32'd0) begin
			status <= STATUS_READ;
			start_cmd <= 1'b1;
			start_read <= 1'b1;
			//CMD17, sector address
			cmd_send_contents <= { 	6'd17,				//command index
									sd_address[31:0]	//sector address
			};
			control_state <= S_CTRL_CMD17_READ;
		end
		else if(control == CONTROL_WRITE && sd_block_count != 32'd0) begin
			status <= STATUS_WRITE;
			start_cmd <= 1'b1;
			start_write <= 1'b1;
			//CMD24, sector address
			cmd_send_contents <= { 	6'd24,				//command index
									sd_address[31:0] 	//sector address
			};
			control_state <= S_CTRL_CMD24_WRITE;
		end
		else begin
			status <= STATUS_IDLE;
		end
	end
	else if(control_state == S_CTRL_CMD17_READ) begin
		if(start_cmd == 1'b1) begin
			start_cmd <= 1'b0;
		end
		else if(cmd_state == S_CMD_REPLY_ERROR ||
			(cmd_state == S_CMD_IDLE &&
				(cmd_reply[47:40] != { 1'b0, 1'b0, 6'd17 } || cmd_reply[39:27] != 13'b0 || cmd_reply[24:21] != 4'b0 ||
				 cmd_reply[13] != 1'b0 || cmd_reply[11] != 1'b0 || cmd_reply[7:0] != { `CRC7_REVERSE, 1'b1 }
				)
			)
		) begin
			error_count <= error_count + 16'd1;
			control_state <= S_CTRL_IDLE;
		end
		else if(cmd_state == S_CMD_IDLE && start_cmd == 1'b0 && start_read == 1'b1) begin
			start_read <= 1'b0;
		end
		else if(cmd_state == S_CMD_IDLE && start_cmd == 1'b0 && start_read == 1'b0 && data_state == S_DATA_READ_ERROR) begin
			error_count <= error_count + 16'd1;
			control_state <= S_CTRL_IDLE;
		end
		else if(cmd_state == S_CMD_IDLE && start_cmd == 1'b0 && start_read == 1'b0 && data_state == S_DATA_IDLE) begin
			error_count <= 16'd0;
			control_state <= S_CTRL_PRE_IDLE;
		end
	end
	else if(control_state == S_CTRL_CMD24_WRITE) begin
		if(start_cmd == 1'b1) begin
			start_cmd <= 1'b0;
		end
		else if(cmd_state == S_CMD_REPLY_ERROR ||
			(cmd_state == S_CMD_IDLE &&
				(cmd_reply[47:40] != { 1'b0, 1'b0, 6'd24 } || cmd_reply[39:27] != 13'b0 || cmd_reply[24:21] != 4'b0 ||
				 cmd_reply[13] != 1'b0 || cmd_reply[11] != 1'b0 || cmd_reply[7:0] != { `CRC7_REVERSE, 1'b1 }
				)
			)
		) begin
			error_count <= error_count + 16'd1;
			control_state <= S_CTRL_IDLE;
		end
		else if(cmd_state == S_CMD_IDLE && start_cmd == 1'b0 && start_write == 1'b1) begin
			start_write <= 1'b0;
		end
		else if(cmd_state == S_CMD_IDLE && start_cmd == 1'b0 && start_write == 1'b0 && data_state == S_DATA_WRITE_ERROR) begin
			error_count <= error_count + 16'd1;
			control_state <= S_CTRL_IDLE;
		end
		else if(cmd_state == S_CMD_IDLE && start_cmd == 1'b0 && start_write == 1'b0 && data_state == S_DATA_IDLE) begin
			error_count <= 16'd0;
			control_state <= S_CTRL_PRE_IDLE;
		end
	end
end
 
//------------------------------------------------------------------------------ SD interface
 
reg sd_cmd_reg = 1'b1;
reg sd_dat_reg = 1'b1;
 
assign sd_cmd = (sd_cmd_enable == 1'b1) ?  sd_cmd_reg : 1'bZ;
assign sd_dat[0] = (sd_data_enable == 1'b1) ? sd_dat_reg : 1'bZ;
 
assign sd_dat[3:1] = 3'bZ;
 
//CID register not interpreted: CRC7 not checked, always accepted
 
//---------------------------------------------------- SD data
 
reg         sd_data_enable;
reg [3:0]   data_state;
reg [23:0]  data_counter;
reg [6:0]   part_counter;
reg [15:0]  crc16;
reg [31:0]  data_part;
 
reg         clk_data_ena;
reg         clk_master_ena;
 
 
localparam [3:0]	S_DATA_IDLE 								= 4'd0;
localparam [3:0]	S_DATA_READ_START_BIT 						= 4'd1;
localparam [3:0]	S_DATA_READ_CONTENTS 						= 4'd2;
localparam [3:0]	S_DATA_READ_READY_PART 						= 4'd3;
localparam [3:0]	S_DATA_READ_READY_PART_CONTINUE				= 4'd4;
localparam [3:0]	S_DATA_READ_CRC16_END_BIT 					= 4'd5;
localparam [3:0]	S_DATA_READ_ERROR 							= 4'd6;
localparam [3:0]	S_DATA_WRITE_START_BIT 						= 4'd7;
localparam [3:0]	S_DATA_WRITE_READY_PART 					= 4'd8;
localparam [3:0]	S_DATA_WRITE_CONTENTS 						= 4'd9;
localparam [3:0]	S_DATA_WRITE_CRC16_END_BIT 					= 4'd10;
localparam [3:0]	S_DATA_WRITE_CRC_STATUS_START 				= 4'd11;
localparam [3:0]	S_DATA_WRITE_CRC_STATUS_CONTENTS_END_BIT 	= 4'd12;
localparam [3:0]	S_DATA_WRITE_BUSY_START 					= 4'd13;
localparam [3:0]	S_DATA_WRITE_BUSY_WAIT 						= 4'd14;
localparam [3:0]	S_DATA_WRITE_ERROR 							= 4'd15;
 
 
always @(posedge clk or negedge rst_n) begin
	if(rst_n == 1'b0) begin
		sd_data_enable 	<= 1'b0;
		data_state 		<= S_DATA_IDLE;
		data_counter 	<= 24'd0;
		part_counter 	<= 7'd0;
		crc16 			<= 16'd0;
		data_part 		<= 32'd0;
		clk_data_ena	<= 1'b0;
		clk_master_ena 	<= 1'b1;
		sd_dat_reg		<= 1'b1;
	end
	else if(data_state == S_DATA_IDLE) begin
		//do not wait for read command and reply
		if(start_read == 1'b1) begin
			data_state <= S_DATA_READ_START_BIT;
		end
		//wait for write command and reply
		else if(start_write == 1'b1 && start_cmd == 1'b0 && cmd_state == S_CMD_IDLE) begin
			data_state <= S_DATA_WRITE_START_BIT;
		end
	end
	else if(clk_counter == 2'd0) begin
 
		//wait for response and data simultaneously (data read)
		if(data_state == S_DATA_READ_START_BIT) begin
			clk_data_ena <= 1'b1;
 
			if(sd_dat[0] == 1'b0) begin
				crc16 <= { sd_dat[0] ^ crc16[0], crc16[15:12], sd_dat[0] ^ crc16[11] ^ crc16[0], crc16[10:5],
					sd_dat[0] ^ crc16[4] ^ crc16[0], crc16[3:1] };
 
				data_state <= S_DATA_READ_CONTENTS;
				data_counter <= 24'd0;
			end
			else if(data_counter == 24'd65535) begin
				data_state <= S_DATA_READ_ERROR;
				data_counter <= 24'd0;
			end
			else data_counter <= data_counter + 24'd1;
		end
		else if(data_state == S_DATA_READ_CONTENTS) begin
			crc16 <= { sd_dat[0] ^ crc16[0], crc16[15:12], sd_dat[0] ^ crc16[11] ^ crc16[0],
				crc16[10:5], sd_dat[0] ^ crc16[4] ^ crc16[0], crc16[3:1] };
			data_part <= { data_part[30:0], sd_dat[0] };
 
			if(data_counter == 24'd30) begin
				clk_master_ena <= 1'b0;
				data_counter <= data_counter + 24'd1;
			end
			else if(data_counter == 24'd31) begin
				data_state <= S_DATA_READ_READY_PART;
				data_counter <= 24'd0;
			end
			else data_counter <= data_counter + 24'd1;
		end
		else if(data_state == S_DATA_READ_READY_PART) begin
			if(data_read == 1'b1) begin
				clk_master_ena <= 1'b1;
				data_state <= S_DATA_READ_READY_PART_CONTINUE;
			end
		end
		else if(data_state == S_DATA_READ_READY_PART_CONTINUE) begin
			if(part_counter == 7'd127) begin
				data_state <= S_DATA_READ_CRC16_END_BIT;
				part_counter <= 7'd0;
			end
			else begin
				data_state <= S_DATA_READ_CONTENTS;
				part_counter <= part_counter + 7'd1;
			end
		end
		else if(data_state == S_DATA_READ_CRC16_END_BIT) begin
			data_part <= { sd_dat[0], data_part[31:1] };
 
			if(data_counter == 24'd16) begin
				if(data_part[31:16] != crc16[15:0] || sd_dat[0] != 1'b1) begin
					data_state <= S_DATA_READ_ERROR;
					data_counter <= 24'd0;
				end
				else begin
					clk_data_ena <= 1'b0;
					data_state <= S_DATA_IDLE;
					data_counter <= 24'd0;
					crc16 <= 16'd0;
				end
			end
			else data_counter <= data_counter + 24'd1;
		end
		else if(data_state == S_DATA_READ_ERROR) begin
			clk_data_ena <= 1'b0;
			data_state <= S_DATA_IDLE;
			data_counter <= 24'd0;
			crc16 <= 16'd0;
		end
 
		//send data on data line, wait for crc status, wait while busy on data line (data write)
		else if(data_state == S_DATA_WRITE_START_BIT) begin
			sd_dat_reg <= 1'b0;
			crc16 <= { 1'b0 ^ crc16[0], crc16[15:12], 1'b0 ^ crc16[11] ^ crc16[0], crc16[10:5],
				1'b0 ^ crc16[4] ^ crc16[0], crc16[3:1] };
 
			sd_data_enable <= 1'b1;
			clk_data_ena <= 1'b1;
			data_counter <= 24'd0;
			data_state <= S_DATA_WRITE_READY_PART;
		end
		else if(data_state == S_DATA_WRITE_READY_PART) begin
 
			if(data_write == 1'b1) begin
			    clk_data_ena <= 1'b1;
				data_state <= S_DATA_WRITE_CONTENTS;
				data_part <= data_part_contents;
			end
			else begin
			    clk_data_ena <= 1'b0;
			end
		end
		else if(data_state == S_DATA_WRITE_CONTENTS) begin
			sd_dat_reg <= data_part[31];
			crc16 <= { data_part[31] ^ crc16[0], crc16[15:12], data_part[31] ^ crc16[11] ^ crc16[0], crc16[10:5],
				data_part[31] ^ crc16[4] ^ crc16[0], crc16[3:1] };
			data_part <= { data_part[30:0], 1'b0 };
 
			if(data_counter == 24'd31) begin
				data_counter <= 24'd0;
 
				if(part_counter == 7'd127) begin
					part_counter <= 7'd0;
					data_state <= S_DATA_WRITE_CRC16_END_BIT;
				end
				else begin
				    clk_data_ena <= 1'b0;
					part_counter <= part_counter + 7'd1;
					data_state <= S_DATA_WRITE_READY_PART;
				end
			end
			else data_counter <= data_counter + 24'd1;
		end
 
		else if(data_state == S_DATA_WRITE_CRC16_END_BIT) begin
			sd_dat_reg <= crc16[0];
 
			if(data_counter == 24'd16) begin
			    data_counter <= 24'd0;
			    crc16 <= 16'd0;
				data_state <= S_DATA_WRITE_CRC_STATUS_START;
			end
			else begin
				crc16 <= { 1'b1, crc16[15:1] };
				data_counter <= data_counter + 24'd1;
			end
 
		end
		else if(data_state == S_DATA_WRITE_CRC_STATUS_START) begin
			sd_data_enable <= 1'b0;
 
			if(sd_dat[0] == 1'b0) begin
				data_state <= S_DATA_WRITE_CRC_STATUS_CONTENTS_END_BIT;
				data_counter <= 24'b0;
			end
			else if(data_counter == 24'd65535) begin
				data_state <= S_DATA_WRITE_ERROR;
				data_counter <= 24'b0;
			end
			else data_counter <= data_counter + 24'd1;
		end
 
		else if(data_state == S_DATA_WRITE_CRC_STATUS_CONTENTS_END_BIT) begin
			data_part <= { data_part[30:0], sd_dat[0] };
 
			if(data_counter == 24'd3) begin
				data_state <= S_DATA_WRITE_BUSY_START;
				data_counter <= 24'b0;
			end
			else data_counter <= data_counter + 24'd1;
		end
		else if(data_state == S_DATA_WRITE_BUSY_START) begin
 
			if(sd_dat[0] == 1'b0) begin
				data_state <= S_DATA_WRITE_BUSY_WAIT;
				data_counter <= 24'b0;
			end
			else if(data_counter == 24'd65535) begin
				data_state <= S_DATA_WRITE_ERROR;
				data_counter <= 24'b0;
			end
			else data_counter <= data_counter + 24'd1;
		end
		else if(data_state == S_DATA_WRITE_BUSY_WAIT) begin
			if(sd_dat[0] == 1'b1 && data_part[3:0] != 4'b0101) begin
				data_state <= S_DATA_WRITE_ERROR;
				data_counter <= 24'd0;
			end
			else if(sd_dat[0] == 1'b1) begin
				clk_data_ena <= 1'b0;
				data_state <= S_DATA_IDLE;
				data_counter <= 24'd0;
			end
			else if(data_counter == 24'hFFFFFF) begin
				data_state <= S_DATA_WRITE_ERROR;
				data_counter <= 24'd0;
			end
			else data_counter <= data_counter + 24'd1;
		end
		else if(data_state == S_DATA_WRITE_ERROR) begin
			clk_data_ena <= 1'b0;
			data_state <= S_DATA_IDLE;
			data_counter <= 24'd0;
		end
	end
end
 
//------------------------------------------------------------------------------ SD command
 
reg 		sd_cmd_enable;
reg [37:0] 	cmd_send;
reg [47:0] 	cmd_reply;
reg [3:0] 	cmd_state;
reg [7:0] 	cmd_counter;
reg [6:0] 	crc7;
reg 		clk_cmd_ena;
 
localparam [3:0]	S_CMD_IDLE 					= 4'd0;
localparam [3:0]	S_CMD_SEND_START_ONES 		= 4'd1;
localparam [3:0]	S_CMD_SEND_START_BIT 		= 4'd2;
localparam [3:0]	S_CMD_SEND_START_HOST 		= 4'd3;
localparam [3:0]	S_CMD_SEND_CONTENTS 		= 4'd4;
localparam [3:0]	S_CMD_SEND_CRC7 			= 4'd5;
localparam [3:0]	S_CMD_SEND_END_BIT 			= 4'd6;
localparam [3:0]	S_CMD_SEND_END_ONES 		= 4'd7;
localparam [3:0]	S_CMD_REPLY_START_BIT 		= 4'd8;
localparam [3:0]	S_CMD_REPLY_CONTENTS 		= 4'd9;
localparam [3:0]	S_CMD_REPLY_CRC7_END_BIT 	= 4'd10;
localparam [3:0]	S_CMD_REPLY_FINISH_ONES 	= 4'd11;
localparam [3:0]	S_CMD_REPLY_ERROR 			= 4'd12;
 
always @(posedge clk or negedge rst_n) begin
	if(rst_n == 1'b0) begin
		sd_cmd_enable 	<= 1'b0;
		cmd_send 		<= 38'd0;
		cmd_reply 		<= 48'd0;
		cmd_state 		<= S_CMD_IDLE;
		cmd_counter 	<= 8'd0;
		crc7 			<= 7'd0;
		clk_cmd_ena 	<= 1'b0;
		sd_cmd_reg		<= 1'b1;
	end
	else if(cmd_state == S_CMD_IDLE) begin
		if(start_cmd == 1'b1) begin
			cmd_state <= S_CMD_SEND_START_ONES;
		end
	end
	else if(clk_counter == 2'd0 && clk_master_ena == 1'b1) begin
 
		//send command
		if(cmd_state == S_CMD_SEND_START_ONES) begin
			sd_cmd_enable <= 1'b1;
			sd_cmd_reg <= 1'b1;
			clk_cmd_ena <= 1'b1;
			crc7 <= 7'd0;
 
			if(cmd_counter == 8'd7) begin
				cmd_state <= S_CMD_SEND_START_BIT;
				cmd_counter <= 8'd0;
			end
			else cmd_counter <= cmd_counter + 8'd1;
		end
		else if(cmd_state == S_CMD_SEND_START_BIT) begin
			sd_cmd_reg <= 1'b0;
			crc7 <= { 1'b0 ^ crc7[0], crc7[6:5], 1'b0 ^ crc7[4] ^ crc7[0], crc7[3:1] };
 
			cmd_state <= S_CMD_SEND_START_HOST;
		end
		else if(cmd_state == S_CMD_SEND_START_HOST) begin
			sd_cmd_reg <= 1'b1;
			crc7 <= { 1'b1 ^ crc7[0], crc7[6:5], 1'b1 ^ crc7[4] ^ crc7[0], crc7[3:1] };
 
			cmd_send <= cmd_send_contents;
			cmd_state <= S_CMD_SEND_CONTENTS;
		end
		else if(cmd_state == S_CMD_SEND_CONTENTS) begin
			sd_cmd_reg <= cmd_send[37];
			crc7 <= { cmd_send[37] ^ crc7[0], crc7[6:5], cmd_send[37] ^ crc7[4] ^ crc7[0], crc7[3:1] };
			cmd_send <= { cmd_send[36:0], 1'b0 };
 
			if(cmd_counter == 8'd37) begin
				cmd_state <= S_CMD_SEND_CRC7;
				cmd_counter <= 8'd0;
			end
			else cmd_counter <= cmd_counter + 8'd1;
		end
		else if(cmd_state == S_CMD_SEND_CRC7) begin
			sd_cmd_reg <= crc7[0];
			crc7 <= { 1'b0, crc7[6:1] };
 
			if(cmd_counter == 8'd6) begin
				cmd_state <= S_CMD_SEND_END_BIT;
				cmd_counter <= 8'd0;
			end
			else cmd_counter <= cmd_counter + 8'd1;
		end
		else if(cmd_state == S_CMD_SEND_END_BIT) begin
			sd_cmd_reg <= 1'b1;
 
			// if CMD0: send ones
			if(control_state == S_CTRL_CMD0) begin
				cmd_state <= S_CMD_SEND_END_ONES;
			end
			else begin
				crc7 <= 7'd0;
				cmd_state <= S_CMD_REPLY_START_BIT;
			end
		end
		else if(cmd_state == S_CMD_SEND_END_ONES) begin
			sd_cmd_enable <= 1'b0;
			sd_cmd_reg <= 1'b1;
 
			if(cmd_counter == 8'd7) begin
				clk_cmd_ena <= 1'b0;
				cmd_state <= S_CMD_IDLE;
				cmd_counter <= 8'd0;
			end
			else cmd_counter <= cmd_counter + 8'd1;
		end
 
		//wait for response: 48-bits with CRC7
		//wait for response: 48-bits without CRC7
		//wait for response: 136-bits (CMD2/R2)
		//wait for response and busy on data line simultaneously: (CMD7/R1b)
		else if(cmd_state == S_CMD_REPLY_START_BIT) begin
			sd_cmd_enable <= 1'b0;
 
			if(sd_cmd == 1'b0) begin
				crc7 <= { sd_cmd ^ crc7[0], crc7[6:5], sd_cmd ^ crc7[4] ^ crc7[0], crc7[3:1] };
				cmd_reply <= { cmd_reply[46:0], sd_cmd };
 
				cmd_state <= S_CMD_REPLY_CONTENTS;
				cmd_counter <= 8'd0;
			end
			else if(cmd_counter == 8'd255) begin
				crc7 <= 7'd0;
				cmd_state <= S_CMD_REPLY_ERROR;
				cmd_counter <= 8'd0;
			end
			else cmd_counter <= cmd_counter + 8'd1;
		end
		else if(cmd_state == S_CMD_REPLY_CONTENTS) begin
			crc7 <= { sd_cmd ^ crc7[0], crc7[6:5], sd_cmd ^ crc7[4] ^ crc7[0], crc7[3:1] };
			cmd_reply <= { cmd_reply[46:0], sd_cmd };
 
			if(	(control_state != S_CTRL_CMD2 && cmd_counter == 8'd38) ||
				(control_state == S_CTRL_CMD2 && cmd_counter == 8'd126)
			) begin
				cmd_state <= S_CMD_REPLY_CRC7_END_BIT;
				cmd_counter <= 8'd0;
			end
			else cmd_counter <= cmd_counter + 8'd1;
		end
		else if(cmd_state == S_CMD_REPLY_CRC7_END_BIT) begin
			cmd_reply <= { cmd_reply[46:0], sd_cmd };
 
			if(cmd_counter == 8'd7) begin
				cmd_state <= S_CMD_REPLY_FINISH_ONES;
				cmd_counter <= 8'd0;
			end
			else cmd_counter <= cmd_counter + 8'd1;
		end
		//at least 2 clock cycles required for data write
		else if(cmd_state == S_CMD_REPLY_FINISH_ONES) begin
			//check is sd_dat[0] busy for CMD7
			if(cmd_counter >= 8'd7 && (control_state != S_CTRL_CMD7 || sd_dat[0] == 1'b1)) begin
				clk_cmd_ena <= 1'b0;
				cmd_state <= S_CMD_IDLE;
				cmd_counter <= 8'd0;
			end
			else if(cmd_counter == 8'd255) begin
				cmd_state <= S_CMD_REPLY_ERROR;
				cmd_counter <= 8'd0;
			end
			else cmd_counter <= cmd_counter + 8'd1;
		end
		else if(cmd_state == S_CMD_REPLY_ERROR) begin
			clk_cmd_ena <= 1'b0;
			cmd_state <= S_CMD_IDLE;
			cmd_counter <= 8'd0;
		end	
	end
end
 
//------------------------------------------------------------------------------ SD clock
 
reg [1:0] clk_counter;
 
always @(posedge clk or negedge rst_n) begin
	if(rst_n == 1'b0) begin
		sd_clk      <= 1'b0;
		clk_counter <= 2'd0;
	end
	else if(clk_counter == 2'd0) begin
		if(clk_master_ena == 1'b1 && (clk_cmd_ena == 1'b1 || clk_data_ena == 1'b1)) begin
			clk_counter <= clk_counter + 2'd1;
		end
	end
	else if(clk_counter == 2'd1) begin
		sd_clk      <= 1'b1;
		clk_counter <= clk_counter + 2'd1;
	end
	else if(clk_counter == 2'd2) begin
	    clk_counter <= clk_counter + 2'd1;
	end
	else if(clk_counter == 2'd3) begin
		sd_clk      <= 1'b0;
		clk_counter <= clk_counter + 2'd1;
	end
end
 
endmodule
 
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.