なんとなく調べてたら、SDカードのアクセスにはめんどくさいモードとめんどくさくないモードがあって、めんどくさくないモードのSPIというのを使うと楽だよって書いてあったので試してみたら、SDカードの初期化まですんなり動いた。
初期化といっても、SDカードの内容を初期化するんじゃなくて、デバイスの初期化のほう。
SDカードの仕様は、DE0に付属のものをみてもサパーリわからんかったのだけど、このあたりを見るとわかりやすかった。
ELM - MMCの使いかた
naru マルチメディアカード(MMC)の使い方
SDカードを使ってみよう
初期化手順は、74クロック待って、CMD0を送って0x01を待って、CMD1を送って0x01が返ってきたらCMD1を再送、0x00が返ってきたら初期化終了、という流れ。
で動かすと、無事にSDカード初期化終了をあらわすコード0x00が返ってきた。左2ケタがSDカードからのレスポンス。右は内部的な処理フェーズで5なら正常終了。
このままCMD17とかCMD18とかを送るとデータが読み出せそうなのだけど、512バイト単位とかになるので、RAMを使えるようにしないと処理が書けないということで、ここで終了。
あと、SPIモードは遅いらしいんで、ゆくゆくはMMCモードを考えたほうがいいのかなと思ったのだけど、そもそもDE0からはSPIモード用の配線しかSDカードにつながってないので、めんどくさいことは考えなくてよかった。
ともあれ、FPGAさわりはじめて1ヶ月くらいずっと触ってて、ようやく仕様からコードがすんなり書けるようになりました。あとはSDRAMとかFLASHのアクセスだけど、これもSDカードと似たような処理でできそう。
ただ、いまのところシミュレーションとかロジックアナライザとか使ってなくてコードだけ見て書いてるので、ほんとにハマったときには対処できないだろうなー。
一応ソース。hexsegモジュールは、適当な7セグ表示のモジュールということで。
`define LAUNCH 1 `define COMMAND 2 `define RECV_WAIT 3 `define RECEIVE 4 `define FIN 5 `define START 2'b01 `define CMD0 6'b000000 `define CMD0CRC 7'b1001010 `define CMD1 6'b000001 `define CRC0 7'b0000000 `define STOP 1'b1 module sdcard( input clk, input [9:0] sw, input [2:0] btn, output [9:0] led, output [7:0] hled0, output [7:0] hled1, output [7:0] hled2, output [7:0] hled3, output sd_pcs,//W21 output sd_pdi,//Y22 output sd_clk,//Y21 input sd_do,//AA22 input sd_wr //W20 ); assign led[0] = sd_wr; assign led[9:2] =9'h0; assign hled3 = 8'hff; reg [2:0] phase = `LAUNCH; reg [1:0] scnt; //create clock always @(posedge clk) begin scnt = scnt + 1; end reg con = 1'b1; reg sd_cs; reg sd_di; assign sd_pcs = sd_cs; assign sd_pdi = sd_di; assign led[1] = !con; assign sd_clk = con ? scnt[1] : 1'b1; always @(posedge scnt[1]) begin if(phase == `RECV_WAIT) begin if(!sd_do) begin rdata <= 8'd0; end end else if(phase == `RECEIVE) begin rdata <= {rdata[6:0], sd_do}; end end reg [6:0] cnt; reg [47:0] sdata; reg [7:0] rdata; reg [6:0] command; always @(negedge scnt[1]) begin if(phase == `LAUNCH) begin //init if(cnt == 0) begin sd_di <= 1'b1; sd_cs <= 1'b1; cnt <= cnt + 1; end else if(cnt == 7'd73) begin sd_cs <= 1'b0; cnt <= 0; phase <= `COMMAND; command <= `CMD0; sdata <= {`START, `CMD0, 32'd0, `CMD0CRC, `STOP}; end else begin cnt <= cnt + 1; end end else if(phase == `COMMAND)begin sd_di <= sdata[47]; sdata <= {sdata[46:0], 1'b0}; if(cnt == 7'd47) begin cnt <= 0; phase <= `RECV_WAIT; end else begin cnt <= cnt + 1; end end else if(phase == `RECV_WAIT) begin if(!sd_do) begin phase <= `RECEIVE; cnt <= 1; end end else if(phase == `RECEIVE) begin if(cnt == 7'd7) begin cnt <= 0; if(command == `CMD0) begin command = `CMD1; sdata <= {`START, `CMD1, 32'd0, `CRC0, `STOP}; phase <= `COMMAND; end else if(command == `CMD1) begin if(rdata == 8'd0) begin //ready phase <= `FIN; end else begin //busy sdata <= {`START, `CMD1, 32'd0, `CRC0, `STOP}; phase <= `COMMAND; end end end else begin cnt <= cnt + 1; end end else begin con <= 1'b0; sd_cs <= 1'b1; end end hexseg h1(clk, rdata[3:0], hled0); hexseg h2(clk, rdata[7:4], hled1); hexseg h3(clk, phase, hled2); endmodule