DE0でFPGAからSDカードにアクセスする

なんとなく調べてたら、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