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