以前は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