CPLDで、SDカード(Ver.1)とSPIモードで通信(初期化まで)

移転しました。

以前はFT245RLを使って、PCのUSBコネクタ経由でSDカードとSPIモードで通信しました。今度は、CPLDを使って、SDカード(Ver.1)とSPIモードで通信してみました。初期化までを行いました。
いまいちVerilogの書き方とか、順序回路のタイミングなどがわかっていないので、効率がとても悪そう。functionを使わずなんでもalwaysで書いてるので、ラッチができていそう。でも今回は、回路がCPLDに納まって、かつ動けば良いという方針で行きます。


無事通信出来ました。CMD0 -> CMD1 -> CMD1

verilog HDLコードはこちら。170 LE

mov.v

module mov(
	output CSX, CLK, DI,
	input DO, rstx
);
//clk
wire clk_5M5;
rc_osc rc_osc( rstx, clk_5M5 );//5.5 MHz

//reg rstx;
wire en;
wire [7:0] data;

//sd
sd_cont sd_cont( clk_5M5, rstx, CSX, CLK, DI, DO, data, en );

//lcd etc.

endmodule


sd_cont.v

module sd_cont(
	input clk_5M5, rstx,
	output reg CSX,
	output CLK, DI,
	input DO,
	output reg [7:0] out_data,
	output en
);

wire [7:0] read_byte;
reg [7:0] cmd, cmd_crc, cmd_res, write_byte, req_res;
reg [31:0] cmd_addr;
wire spi_en_wr, spi_en_rd;
reg clk_400k;
reg [3:0] clk_cnt_400k;
reg [7:0] dummy_cnt;
wire clk_slow;
reg [3:0] state, state_aft_w;
reg [2:0] write_state;
reg [15:0] wait_ms, wait_cnt;
reg [3:0] ws_time_out;
reg csx;
assign en = 1'b0;

spi spi(csx, CLK, DO, DI, write_byte, read_byte, spi_en_wr, spi_en_rd);

`define MS_CNT ( clk_slow ? 14'd0400 : 14'd5555 ) // 1e-3 s * 5.5e6 Hz = 5.5e3
`define PON 4'h0
`define DUMMY_CLK 4'h1
`define SOFT_RST 4'h2
`define SOFT_RST_RES 4'h3
`define INIT 4'h4
`define INIT_RES 4'h5
`define IDOL 4'h6
`define CMD_WRITE 4'h7
`define WAIT 4'h8

`define CMD_BIT 8'b01000000
/*cmd write state*/
`define WS_CMD 3'h0
`define WS_ADDR1 3'h1
`define WS_ADDR2 3'h2
`define WS_ADDR3 3'h3
`define WS_ADDR4 3'h4
`define WS_CRC 3'h5
`define WS_RES 3'h6
`define WS_TIME_OUT 4'd10

//400 kHz in init 5.5e6/400e3 ~ 14
always @ ( posedge clk_5M5 or negedge rstx ) begin
	if ( !rstx ) begin
		clk_cnt_400k <= 4'h0;
		clk_400k <= 1'b0;
	end else if ( clk_cnt_400k < 4'he ) begin
		clk_cnt_400k <= clk_cnt_400k + 1'b1;
		clk_400k <= clk_400k;
	end else begin
		clk_cnt_400k <= 4'h0;
      clk_400k <= ~clk_400k;
	end
end

//clk supply to SD
/*
always @ ( clk_400k or clk_5M5 or state_aft_w )
	if ( state_aft_w <= `INIT ) begin
		CLK <= clk_400k;
		clk_slow <= 1'b1;
	end else begin
		CLK <= clk_5M5;
		clk_slow <= 1'b0;
	end
*/
assign CLK = clk_400k;
assign clk_slow = 1'b1;

/*
function fclk_slow;
input [2:0] state_aft_w;
begin
	case ( state_aft_w )
		3'h0,3'h1,3'h2,3'h3: fclk_slow = 1'b1;
		default: fclk_slow = 1'b0;
	endcase
end
endfunction
assign clk_slow = fclk_slow( state_aft_w );
*/

always @ ( negedge CLK )
	CSX <= csx;

//jtag_probe jtag_probe({spi_en_wr, state, state_aft_w, write_state, ws_time_out, CLK});

// state, CSX, write_byte, read_byte, spi_en_wr
always @ ( posedge CLK or negedge rstx ) begin
	if ( !rstx ) begin
		state <= `PON;
		dummy_cnt <= 1'b0;
		write_state <= `WS_CMD;
		wait_cnt <= 1'b0;
		csx <= 1'b1;
		wait_ms <= 16'h0;
		write_byte <= 8'hff;
	end else begin
		case ( state )
			`PON: begin
				csx <= 1'b1;
				state_aft_w <= `DUMMY_CLK;
				state <= `WAIT;
				wait_ms <= 16'h1; // power on and wait > 1ms
				write_byte <= 8'hff;
				dummy_cnt <= 1'b0;
			end
			`DUMMY_CLK: begin //dummy clock > 74 with CSX = 1, 400kHz
				csx <= 1'b1;
				if ( dummy_cnt > 8'd74 ) begin
					state <= `SOFT_RST;
					dummy_cnt <= 1'b0;
				end else begin
					dummy_cnt <= dummy_cnt + 1'b1;
				end
			end
			`SOFT_RST: begin// CMD0 with CSX = 0
				cmd <= 6'b000000;
				cmd_addr <= 32'b0;
				cmd_crc <= 7'b1001010;
				state <= `CMD_WRITE;
				state_aft_w <= `SOFT_RST_RES;
				csx <= 1'b1;
			end
			`SOFT_RST_RES: begin
				if ( cmd_res == 8'h01 )
					state <= `INIT;
				else
					state <= `PON;
			end
			`CMD_WRITE: begin
				csx <= 1'b0;
				if ( spi_en_wr ) begin
					case ( write_state )
						`WS_CMD: begin
							write_byte <= ( cmd | `CMD_BIT );
							write_state <= `WS_ADDR1;
						end
						`WS_ADDR1: begin
							write_byte <= ( cmd_addr >> 24 );//truncate upper bits
							write_state <= `WS_ADDR2;
						end
						`WS_ADDR2: begin
							write_byte <= ( cmd_addr >> 16 );//truncate upper bits
							write_state <= `WS_ADDR3;
						end
						`WS_ADDR3: begin
							write_byte <= ( cmd_addr >> 8 );//truncate upper bits
							write_state <= `WS_ADDR4;
						end
						`WS_ADDR4: begin
							write_byte <= cmd_addr;//truncate upper bits
							write_state <= `WS_CRC;
						end
						`WS_CRC: begin
							write_byte <= (( {1'b0, cmd_crc} << 1 ) | 1'b1 );
							write_state <= `WS_RES;
						end
					endcase
				end else if ( spi_en_rd & ( write_state == `WS_RES ) ) begin
					if ( read_byte != 8'hff ) begin
						write_state <= `WS_CMD;
						state <= state_aft_w;
						cmd_res <= read_byte;
						ws_time_out <= 1'b0;
					end else if ( ws_time_out > `WS_TIME_OUT ) begin
						state <= `PON; //restart
						write_state <= `WS_CMD;
						ws_time_out <= 1'b0;
					end else begin
						write_byte <= 8'hff;
						ws_time_out <= ws_time_out + 1'b1;
						write_state <= `WS_RES;
					end
				end
			end
			`INIT: begin //cmd1
				cmd <= 6'h01;
				cmd_addr <= 32'b0;
				cmd_crc <= 1'b0;
				state <= `CMD_WRITE;
				state_aft_w <= `INIT_RES;
			end
			`INIT_RES: begin
				if ( cmd_res == 8'h00 )
					state <= `IDOL;
				else if ( cmd_res == 8'h01 ) //in idol state
					state <= `CMD_WRITE; //retry
				else
					state <= `PON;//restart
			end
			`IDOL: begin
				state_aft_w <= `PON;
				state <= `WAIT;
				wait_ms <= 16'd1000;
				csx <= 1'b1;
			end
			`WAIT: begin
				if ( wait_ms > 1'b0 ) begin
					state <= `WAIT;
					if ( wait_cnt < `MS_CNT ) begin
						wait_cnt <= wait_cnt + 1'b1;
					end else begin
						wait_cnt <= 1'b0;
						wait_ms <= wait_ms - 1'b1;
					end
				end else begin
					state <= state_aft_w;
					wait_cnt <= 1'b0;
				end
			end
			default: begin
				state <= `IDOL;
			end
		endcase
	end
end
endmodule


spi.v

module spi(
input csx, CLK, MISO,
output reg MOSI,
input [7:0] inbyte,
output reg [7:0] outbyte,
output en_wr, en_rd
);

reg [2:0] cnt;
assign en_wr = ( cnt == 3'b111 ) | csx;
assign en_rd = ( cnt == 3'b000 );
//reg [7:0] data_wr;

always @ ( posedge CLK ) begin
	//if ( CSX )
	//outbyte <= 8'hff;
	//else
	outbyte <= ( outbyte << 1 ) | MISO;
	//outbyte[7-cnt] <= MISO;
end

/*always @ ( negedge en_wr ) begin //polling inbyte
	data_wr <= inbyte;
end*/

always @ ( negedge CLK ) begin
	//{MOSI, data_wr} <= ( {1'b0, data_wr} << 1 );
	MOSI <= inbyte[7-cnt];
end

always @ ( posedge CLK ) begin
	if ( csx )
		cnt <= 3'h0;
	else
		cnt <= cnt + 1'b1;

end


endmodule