§ Команды

Активируется команда путем записи в sd_cmd значения:
00  Инициализация устройства в режим SPI (80 единиц)
01  Прием и передача данных из sd_din, в sd_out
02  Включить чип (CS=0)
03  Выключить чип (CS=1)
Активируется позитивным фронтом сигнала sd_signal, но при этом sd_busy должен быть равен 0!
При sd_timeout=1 необходимо снова выслать sd_cmd=00 (init).
Для чтения данных используется sd_din=FFh, sd_cmd=1, и позитивный фронт sd_signal.
Максимальная скорость передачи данных 12,5 мегабит (~1.5 мегабайт в сек).

§ Top-уровень

Этот код предназначен для DE0-CV.
assign SD_DATA[0] = 1'bZ;

wire [1:0]  sd_cmd;
wire [7:0]  sd_din;
wire [7:0]  sd_out;
wire        sd_signal;
wire        sd_busy;
wire        sd_timeout;

sd UnitSD
(
    // 50 Mhz
    .clock50    (CLOCK_50),

    // Физический интерфейс
    .SPI_CS     (SD_DATA[3]),   // Выбор чипа
    .SPI_SCLK   (SD_CLK),       // Тактовая частота
    .SPI_MISO   (SD_DATA[0]),   // Входящие данные
    .SPI_MOSI   (SD_CMD),       // Исходящие

    // Интерфейс
    .sd_signal  (sd_signal),   // In   =1 Сообщение отослано на spi
    .sd_cmd     (sd_cmd),      // In      Команда
    .sd_din     (sd_din),      // Out     Принятое сообщение от карты
    .sd_out     (sd_out),      // In      Сообщение на отправку к карте
    .sd_busy    (sd_busy),     // Out  =1 Занято
    .sd_timeout (sd_timeout)   // Out  =1 Таймаут
);

§ Код модуля

module sd
(
    // 50 Mhz
    input  wire         clock50,

    // SPI
    output reg          SPI_CS,
    output reg          SPI_SCLK,
    input  wire         SPI_MISO,
    output reg          SPI_MOSI,

    // Интерфейс
    input  wire         sd_signal,  // 0->1 Команда на позитивном фронте
    input  wire [ 1:0]  sd_cmd,     // ID команды
    output reg  [ 7:0]  sd_din,     // Исходящие данные в процессор
    input  wire [ 7:0]  sd_out,     // Входящие данные из процессора
    output reg          sd_busy,    // =1 Устройство занято
    output wire         sd_timeout  // =1 Вышел таймаут
);

`define SPI_TIMEOUT_CNT     5000000     // 0.1 s

initial begin

    SPI_CS   = 1'b1;
    SPI_SCLK = 1'b0;
    SPI_MOSI = 1'b0;
    sd_din   = 8'h00;
    sd_busy  = 1'b0;

end

// ---------------------------------------------------------------------
// SPI SdCard
// ---------------------------------------------------------------------

// Сигналы нейтрализации (сброс активации команды)
reg  [1:0]  spi_latch   = 2'b00;

// Сигнал о том, занято ли устройство
assign      sd_timeout = (sd_timeout_cnt == `SPI_TIMEOUT_CNT);

reg  [2:0]  spi_process = 0;
reg  [3:0]  spi_cycle   = 0;
reg  [7:0]  spi_data_w  = 0;

// INIT SPI MODE
reg  [7:0]  spi_counter   = 0;
reg  [7:0]  spi_slow_tick = 0;
reg  [24:0] sd_timeout_cnt = `SPI_TIMEOUT_CNT;

always @(posedge clock50) begin

    // Счетчик таймаута. Дойдя для
    if (sd_timeout_cnt < `SPI_TIMEOUT_CNT && spi_process == 0)
        sd_timeout_cnt <= sd_timeout_cnt + 1;

    case (spi_process)

        // Инициировать процессинг
        0: if (spi_latch == 2'b01) begin

            spi_process <= 1 + sd_cmd;
            spi_counter <= 0;
            spi_cycle   <= 0;
            spi_data_w  <= sd_out;
            sd_busy     <= 1;
            sd_timeout_cnt <= 0;

        end

        // Command-1: 80 тактов в slow-режиме
        1: begin

            SPI_CS   <= 1;
            SPI_MOSI <= 1;

            // 250*100`000
            if (spi_slow_tick == (250 - 1)) begin

                SPI_SCLK      <= ~SPI_SCLK;
                spi_slow_tick <= 0;
                spi_counter   <= spi_counter + 1;

                // 80 ticks
                if (spi_counter == (2*80 - 1)) begin

                    SPI_SCLK    <= 0;
                    spi_process <= 0;
                    sd_busy     <= 0;

                end

            end
            // Оттикивание таймера
            else begin spi_slow_tick <= spi_slow_tick + 1;  end

        end

        // Command 1: Read/Write SPI
        2: case (spi_cycle)

            // CLK-DN
            0: begin spi_cycle <= 1; SPI_SCLK <= 0; SPI_MOSI <= 0; end
            1: begin spi_cycle <= 2; SPI_MOSI <= spi_data_w[7]; end
            // CLK-UP
            2: begin spi_cycle <= 3; SPI_SCLK <= 1; spi_counter <= spi_counter + 1; end
            3: begin

                spi_cycle  <= 0;
                sd_din     <= {sd_din[6:0], SPI_MISO};
                spi_data_w <= {spi_data_w[6:0], 1'b0};

                if (spi_counter == 8) begin

                    SPI_SCLK    <= 0;
                    sd_busy     <= 0;
                    spi_counter <= 0;
                    spi_process <= 0;
                    SPI_MOSI    <= 0;

                end
            end

        endcase

        // Переключиться за 2 такта, чтобы среагировал CPU
        3: case (spi_cycle)

            0: spi_cycle <= 1;
            1: spi_cycle <= 2;
            2: begin SPI_CS <= 1'b0; spi_process <= 0; sd_busy <= 0; end

        endcase

        4: case (spi_cycle)

            0: spi_cycle <= 1;
            1: spi_cycle <= 2;
            2: begin SPI_CS <= 1'b1; spi_process <= 0; sd_busy <= 0; end

        endcase

    endcase

    // Активизация работы устройства
    spi_latch <= {spi_latch[0], sd_signal};

end

endmodule