URL
https://opencores.org/ocsvn/ao68000/ao68000/trunk
Subversion Repositories ao68000
[/] [ao68000/] [trunk/] [tests/] [soc_for_linux_on_terasic_de2_70/] [verilog/] [sd.v] - Rev 12
Compare with Previous | Blame | View Log
/* * Copyright 2010, Aleksander Osman, alfik@poczta.fm. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. 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 AUTHOR ``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 AUTHOR 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 sd( input CLK_I, input RST_I, output reg CYC_O, output reg [31:0] DAT_O, output reg STB_O, output reg WE_O, output [31:2] ADR_O, output [3:0] SEL_O, input [31:0] DAT_I, input ACK_I, input ERR_I, input RTY_I, // TAG_TYPE: TGC_O output SGL_O, output BLK_O, output RMW_O, // TAG_TYPE: TGA_O output [2:0] CTI_O, output [1:0] BTE_O, //slave output [31:0] slave_DAT_O, input [31:0] slave_DAT_I, output reg ACK_O, output ERR_O, output RTY_O, input CYC_I, input [3:2] ADR_I, input STB_I, input WE_I, input [3:0] SEL_I, //sd bus 1-bit interface output reg sd_clk_o = 1'b0, inout sd_cmd_io, inout sd_dat_io, output [5:0] debug_leds ); /*********************************************************************************************************************** * Wishbone interface **********************************************************************************************************************/ assign debug_leds = { (RST_I == 1'b1), (error_count == 16'd65535), control_state }; //---------------------------------------------------- wishbone master assign SGL_O = 1'b1; assign BLK_O = 1'b0; assign RMW_O = 1'b0; assign SEL_O = 4'b1111; assign BTE_O = 2'b00; assign CTI_O = 3'b000; assign ADR_O = wb_address[31:2] + { 23'b0, part_counter }; reg bus_error; reg data_read = 1'b0; reg data_write = 1'b0; reg [31:0] data_part_contents; //STB_O,CYC_O,WE_O,DAT_O, always @(posedge CLK_I) begin if(RST_I == 1'b1) begin STB_O <= 1'b0; CYC_O <= 1'b0; WE_O <= 1'b0; DAT_O <= 32'd0; bus_error <= 1'b0; data_read <= 1'b0; data_write <= 1'b0; data_part_contents <= 32'd0; end else if(data_state == S_DATA_READ_READY_PART && data_read == 1'b0) begin if(ACK_I == 1'b1) begin STB_O <= 1'b0; CYC_O <= 1'b0; WE_O <= 1'b0; data_read <= 1'b1; end else if(RTY_I == 1'b1) begin STB_O <= 1'b0; CYC_O <= 1'b0; WE_O <= 1'b0; end else if(ERR_I == 1'b1) begin STB_O <= 1'b0; CYC_O <= 1'b0; WE_O <= 1'b0; data_read <= 1'b1; bus_error <= 1'b1; end else begin STB_O <= 1'b1; CYC_O <= 1'b1; WE_O <= 1'b1; DAT_O <= data_part; end end else if(data_state == S_DATA_WRITE_READY_PART && data_write == 1'b0) begin if(ACK_I == 1'b1) begin STB_O <= 1'b0; CYC_O <= 1'b0; WE_O <= 1'b0; data_part_contents <= DAT_I; data_write <= 1'b1; end else if(RTY_I == 1'b1) begin STB_O <= 1'b0; CYC_O <= 1'b0; WE_O <= 1'b0; end else if(ERR_I == 1'b1) begin STB_O <= 1'b0; CYC_O <= 1'b0; WE_O <= 1'b0; data_write <= 1'b1; bus_error <= 1'b1; end else begin STB_O <= 1'b1; CYC_O <= 1'b1; WE_O <= 1'b0; end end else if(data_state != S_DATA_READ_READY_PART && data_state != S_DATA_WRITE_READY_PART) begin if(status == STATUS_ERROR) begin bus_error <= 1'b0; end data_read <= 1'b0; data_write <= 1'b0; end end //---------------------------------------------------- wishbone slave assign ERR_O = 1'b0; assign RTY_O = 1'b0; assign slave_DAT_O = {29'd0, status[2:0]}; //write only reg [31:0] wb_address; reg [31:0] sd_address; reg [31:0] sd_block_count; reg [1:0] control; reg [3:0] last_control_state; always @(posedge CLK_I) begin if(RST_I == 1'b1) begin wb_address <= 32'd0; sd_address <= 32'd0; sd_block_count <= 32'd0; control <= 2'd0; ACK_O <= 1'b0; last_control_state <= 4'd0; end else begin last_control_state <= control_state; if( (last_control_state == S_CTRL_CMD17_READ || last_control_state == S_CTRL_CMD24_WRITE) && control_state == S_CTRL_PRE_IDLE ) begin sd_block_count <= sd_block_count - 32'd1; sd_address <= sd_address + 32'd1; wb_address <= wb_address + 32'd512; ACK_O <= 1'b0; end else if(CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b1) begin ACK_O <= 1'b1; if(ADR_I[3:2] == 2'b00) begin wb_address[31:24] <= (SEL_I[3] == 1'b1) ? slave_DAT_I[31:24] : wb_address[31:24]; wb_address[23:16] <= (SEL_I[2] == 1'b1) ? slave_DAT_I[23:16] : wb_address[23:16]; wb_address[15:8] <= (SEL_I[1] == 1'b1) ? slave_DAT_I[15:8] : wb_address[15:8]; wb_address[7:0] <= (SEL_I[0] == 1'b1) ? slave_DAT_I[7:0] : wb_address[7:0]; end else if(ADR_I[3:2] == 2'b01) begin sd_address[31:24] <= (SEL_I[3] == 1'b1) ? slave_DAT_I[31:24] : sd_address[31:24]; sd_address[23:16] <= (SEL_I[2] == 1'b1) ? slave_DAT_I[23:16] : sd_address[23:16]; sd_address[15:8] <= (SEL_I[1] == 1'b1) ? slave_DAT_I[15:8] : sd_address[15:8]; sd_address[7:0] <= (SEL_I[0] == 1'b1) ? slave_DAT_I[7:0] : sd_address[7:0]; end else if(ADR_I[3:2] == 2'b10) begin sd_block_count[31:24] <= (SEL_I[3] == 1'b1) ? slave_DAT_I[31:24] : sd_block_count[31:24]; sd_block_count[23:16] <= (SEL_I[2] == 1'b1) ? slave_DAT_I[23:16] : sd_block_count[23:16]; sd_block_count[15:8] <= (SEL_I[1] == 1'b1) ? slave_DAT_I[15:8] : sd_block_count[15:8]; sd_block_count[7:0] <= (SEL_I[0] == 1'b1) ? slave_DAT_I[7:0] : sd_block_count[7:0]; end else if(ADR_I[3:2] == 2'b11) begin control[1:0] <= (SEL_I[0] == 1'b1) ? slave_DAT_I[1:0] : control[1:0]; end end else if(CYC_I == 1'b1 && STB_I == 1'b1 && WE_I == 1'b0 && ADR_I[3:2] == 2'b00) begin ACK_O <= 1'b1; end else begin ACK_O <= 1'b0; 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 = 1'b0; reg start_read = 1'b0; reg start_write = 1'b0; `define CRC7_REVERSE crc7[0],crc7[1],crc7[2],crc7[3],crc7[4],crc7[5],crc7[6] parameter [3:0] S_CTRL_INIT = 4'd0, S_CTRL_CMD0 = 4'd1, S_CTRL_CMD8 = 4'd2, S_CTRL_CMD55 = 4'd3, S_CTRL_ACMD41 = 4'd4, S_CTRL_CMD2 = 4'd5, S_CTRL_CMD3 = 4'd6, S_CTRL_CMD7 = 4'd7, S_CTRL_PRE_IDLE = 4'd8, S_CTRL_IDLE = 4'd9, S_CTRL_CMD17_READ = 4'd10, S_CTRL_CMD24_WRITE = 4'd11; parameter [2:0] STATUS_INIT = 3'd0, STATUS_INIT_ERROR = 3'd1, STATUS_IDLE = 3'd2, STATUS_READ = 3'd3, STATUS_WRITE = 3'd4, STATUS_ERROR = 3'd4; parameter [1:0] CONTROL_IDLE = 2'd0, CONTROL_REINIT = 2'd1, CONTROL_READ = 2'd2, CONTROL_WRITE = 2'd3; always @(posedge CLK_I) begin if(RST_I == 1'b1) 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; start_cmd <= 1'b1; if(cmd_state == S_CMD_IDLE) begin //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; if(bus_error == 1'b1) error_count <= 16'd65535; end else if(control_state == S_CTRL_IDLE && error_count == 16'd65535) 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_o = 1'b1; reg sd_dat_o = 1'b1; assign sd_cmd_io = (sd_cmd_enable == 1'b1) ? sd_cmd_o : 1'bZ; assign sd_dat_io = (sd_data_enable == 1'b1) ? sd_dat_o : 1'bZ; //CID register not interpreted: CRC7 not checked, always accepted //---------------------------------------------------- SD data reg sd_data_enable; reg [3:0] data_state; reg [15: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 = 1'b1; parameter [3:0] S_DATA_IDLE = 4'd0, S_DATA_READ_START_BIT = 4'd1, S_DATA_READ_CONTENTS = 4'd2, S_DATA_READ_READY_PART = 4'd3, S_DATA_READ_READY_PART_CONTINUE = 4'd4, S_DATA_READ_CRC16_END_BIT = 4'd5, S_DATA_READ_ERROR = 4'd6, S_DATA_WRITE_START_BIT = 4'd7, S_DATA_WRITE_READY_PART = 4'd8, S_DATA_WRITE_CONTENTS = 4'd9, S_DATA_WRITE_CRC16_END_BIT = 4'd10, S_DATA_WRITE_CRC_STATUS_START = 4'd11, S_DATA_WRITE_CRC_STATUS_CONTENTS_END_BIT = 4'd12, S_DATA_WRITE_BUSY_START = 4'd13, S_DATA_WRITE_BUSY_WAIT = 4'd14, S_DATA_WRITE_ERROR = 4'd15; always @(posedge CLK_I) begin if(RST_I == 1'b1) begin sd_data_enable <= 1'b0; data_state <= S_DATA_IDLE; data_counter <= 16'd0; part_counter <= 7'd0; crc16 <= 16'd0; data_part <= 32'd0; clk_data_ena <= 1'b0; clk_master_ena <= 1'b1; sd_dat_o <= 1'b1; end else if(clk_counter == 2'd0) begin if(data_state == S_DATA_IDLE) begin if(start_read == 1'b1) begin crc16 <= 16'd0; data_state <= S_DATA_READ_START_BIT; end else if(start_write == 1'b1 && start_cmd == 1'b0 && cmd_state == S_CMD_IDLE) begin crc16 <= 16'd0; clk_data_ena <= 1'b1; data_state <= S_DATA_WRITE_START_BIT; end end //wait for response and data simultaneously (data read) else if(data_state == S_DATA_READ_START_BIT) begin clk_data_ena <= 1'b1; if(sd_dat_io == 1'b0) begin crc16 <= { sd_dat_io ^ crc16[0], crc16[15:12], sd_dat_io ^ crc16[11] ^ crc16[0], crc16[10:5], sd_dat_io ^ crc16[4] ^ crc16[0], crc16[3:1] }; data_state <= S_DATA_READ_CONTENTS; data_counter <= 16'd0; end else if(data_counter == 16'd65535) begin crc16 <= 16'd0; data_state <= S_DATA_READ_ERROR; data_counter <= 16'd0; end else data_counter <= data_counter + 16'd1; end else if(data_state == S_DATA_READ_CONTENTS) begin crc16 <= { sd_dat_io ^ crc16[0], crc16[15:12], sd_dat_io ^ crc16[11] ^ crc16[0], crc16[10:5], sd_dat_io ^ crc16[4] ^ crc16[0], crc16[3:1] }; data_part <= { data_part[30:0], sd_dat_io }; if(data_counter == 16'd30) begin clk_master_ena <= 1'b0; data_counter <= data_counter + 16'd1; end else if(data_counter == 16'd31) begin data_state <= S_DATA_READ_READY_PART; data_counter <= 16'd0; end else data_counter <= data_counter + 16'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_io, data_part[31:1] }; if(data_counter == 16'd16) begin if(data_part[31:16] != crc16[15:0] || sd_dat_io != 1'b1) begin data_state <= S_DATA_READ_ERROR; data_counter <= 16'd0; end else begin clk_data_ena <= 1'b0; data_state <= S_DATA_IDLE; data_counter <= 16'd0; end end else data_counter <= data_counter + 16'd1; end else if(data_state == S_DATA_READ_ERROR) begin if(start_read == 1'b1 || start_write == 1'b1) begin clk_data_ena <= 1'b0; data_state <= S_DATA_IDLE; data_counter <= 16'd0; end end //send data on data line, wait for crc status, wait for busy on data line (data write) else if(data_state == S_DATA_WRITE_START_BIT) begin sd_dat_o <= 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'b0; data_counter <= 16'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 data_state <= S_DATA_WRITE_CONTENTS; data_part <= data_part_contents; end end else if(data_state == S_DATA_WRITE_CONTENTS) begin sd_dat_o <= 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 == 16'd31) begin data_counter <= 16'd0; if(part_counter == 7'd127) begin part_counter <= 7'd0; data_state <= S_DATA_WRITE_CRC16_END_BIT; end else begin part_counter <= part_counter + 7'd1; data_state <= S_DATA_WRITE_READY_PART; end end else data_counter <= data_counter + 16'd1; end else if(data_state == S_DATA_WRITE_CRC16_END_BIT) begin sd_dat_o <= crc16[0]; if(data_counter == 16'd17) begin crc16 <= 16'd0; data_state <= S_DATA_WRITE_CRC_STATUS_START; end else begin crc16 <= { 1'b1, crc16[15:1] }; data_counter <= data_counter + 16'd1; end end else if(data_state == S_DATA_WRITE_CRC_STATUS_START) begin sd_data_enable <= 1'b0; if(sd_dat_io == 1'b0) begin data_state <= S_DATA_WRITE_CRC_STATUS_CONTENTS_END_BIT; data_counter <= 16'b0; end else if(data_counter == 16'd65535) begin data_state <= S_DATA_WRITE_ERROR; data_counter <= 16'b0; end else data_counter <= data_counter + 16'd1; end else if(data_state == S_DATA_WRITE_CRC_STATUS_CONTENTS_END_BIT) begin data_part <= { data_part[30:0], sd_dat_io }; if(data_counter == 16'd3) begin data_state <= S_DATA_WRITE_BUSY_START; data_counter <= 16'b0; end else data_counter <= data_counter + 16'd1; end else if(data_state == S_DATA_WRITE_BUSY_START) begin if(sd_dat_io == 1'b0) begin data_state <= S_DATA_WRITE_BUSY_WAIT; data_counter <= 16'b0; end else if(data_counter == 16'd65535) begin data_state <= S_DATA_WRITE_ERROR; data_counter <= 16'b0; end else data_counter <= data_counter + 16'd1; end else if(data_state == S_DATA_WRITE_BUSY_WAIT) begin if(sd_dat_io == 1'b1 && data_part[3:0] != 4'b0010) begin data_state <= S_DATA_WRITE_ERROR; data_counter <= 16'd0; end else if(sd_dat_io == 1'b1) begin clk_data_ena <= 1'b0; data_state <= S_DATA_IDLE; data_counter <= 16'd0; end else if(data_counter == 16'd65535) begin data_state <= S_DATA_WRITE_ERROR; data_counter <= 16'd0; end else data_counter <= data_counter + 16'd1; end else if(data_state == S_DATA_WRITE_ERROR) begin if(start_read == 1'b1 || start_write == 1'b1) begin clk_data_ena <= 1'b0; data_state <= S_DATA_IDLE; data_counter <= 16'd0; end end end end //---------------------------------------------------- SD command reg sd_cmd_enable; reg [37:0] cmd_send; reg [47:0] cmd_reply; reg [7:0] cmd_state; reg [7:0] cmd_counter; reg [6:0] crc7; reg clk_cmd_ena; parameter [7:0] S_CMD_IDLE = 8'd0, S_CMD_SEND_START_ONES = 8'd1, S_CMD_SEND_START_BIT = 8'd2, S_CMD_SEND_START_HOST = 8'd3, S_CMD_SEND_CONTENTS = 8'd4, S_CMD_SEND_CRC7 = 8'd5, S_CMD_SEND_END_BIT = 8'd6, S_CMD_SEND_END_ONES = 8'd7, S_CMD_REPLY_START_BIT = 8'd8, S_CMD_REPLY_CONTENTS = 8'd9, S_CMD_REPLY_CRC7_END_BIT = 8'd10, S_CMD_REPLY_FINISH_ONES = 8'd11, S_CMD_REPLY_ERROR = 8'd12; always @(posedge CLK_I) begin if(RST_I == 1'b1) 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_o <= 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_o <= 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_o <= 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_o <= 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_o <= 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_o <= 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_o <= 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_o <= 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_io == 1'b0) begin crc7 <= { sd_cmd_io ^ crc7[0], crc7[6:5], sd_cmd_io ^ crc7[4] ^ crc7[0], crc7[3:1] }; cmd_reply <= { cmd_reply[46:0], sd_cmd_io }; 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_io ^ crc7[0], crc7[6:5], sd_cmd_io ^ crc7[4] ^ crc7[0], crc7[3:1] }; cmd_reply <= { cmd_reply[46:0], sd_cmd_io }; if( (control_state != S_CTRL_CMD2 && cmd_counter == 8'd38) || (control_state == S_CTRL_CMD2 && cmd_counter == 8'd134) ) 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_io }; 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_io busy for CMD7 if(cmd_counter >= 8'd7 && (control_state != S_CTRL_CMD7 || sd_dat_io == 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 if(start_cmd == 1'b1) begin clk_cmd_ena <= 1'b0; cmd_state <= S_CMD_IDLE; cmd_counter <= 8'd0; end end end end //---------------------------------------------------- SD clock reg [1:0] clk_counter; always @(posedge CLK_I) begin if(RST_I == 1'b1) begin sd_clk_o <= 1'b0; clk_counter <= 2'd0; end else if(clk_counter == 2'd0) begin sd_clk_o <= 1'd0; 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_o <= 1'b1; clk_counter <= clk_counter + 2'd1; end else if(clk_counter == 2'd2) begin //was 5 sd_clk_o <= 1'b0; clk_counter <= clk_counter + 2'd1; end else clk_counter <= clk_counter + 2'd1; end endmodule