|
@@ -0,0 +1,243 @@
|
|
|
+// -----------------------------------------------------------------------
|
|
|
+//
|
|
|
+// Copyright 2003-2021 H. Peter Anvin - All Rights Reserved
|
|
|
+//
|
|
|
+// This program is free software; you can redistribute it and/or modify
|
|
|
+// it under the terms of the GNU General Public License as published by
|
|
|
+// the Free Software Foundation, Inc., 53 Temple Place Ste 330,
|
|
|
+// Bostom MA 02111-1307, USA; either version 2 of the License, or
|
|
|
+// (at your option) any later version; incorporated herein by reference.
|
|
|
+//
|
|
|
+// -----------------------------------------------------------------------
|
|
|
+
|
|
|
+//
|
|
|
+// MMC/SD controller for MAX80
|
|
|
+//
|
|
|
+// This runs the SD card in SPI mode. In the future, consider improving
|
|
|
+// performance by switching to quad SD mode.
|
|
|
+//
|
|
|
+
|
|
|
+module sdcard (
|
|
|
+ input rst_n, // Global reset
|
|
|
+ input clk, // System clock (84 MHz)
|
|
|
+
|
|
|
+ inout sd_cs_n, // SD card CS# (CD, DAT3)
|
|
|
+ inout sd_di, // SD card DI (MOSI, CMD)
|
|
|
+ inout sd_sclk, // SD card CLK (SCLK)
|
|
|
+ inout sd_do, // SD card SO (MISO, DAT0)
|
|
|
+
|
|
|
+ input sd_cd_n, // SD socket CD# (Card Detect) switch
|
|
|
+ input sd_we_n, // SD socket WE# (Write Enable) switch
|
|
|
+
|
|
|
+ input [31:0] wdata, // CPU data out (CPU->controller)
|
|
|
+ output reg [31:0] rdata, // CPU data in (controller->CPU)
|
|
|
+ input valid, // Memory valid
|
|
|
+ input [3:0] wstrb, // Write strobes
|
|
|
+ input [1:0] addr, // Address bits
|
|
|
+ output wait_n // Hold mem_ready
|
|
|
+ );
|
|
|
+
|
|
|
+ // ------------------------------------------------------------------------
|
|
|
+ // SD card interface
|
|
|
+ //
|
|
|
+ // This drives the SD card in SPI mode. We support two speeds:
|
|
|
+ // 84 MHz/4 = 21 MHz for normal operation, and 84 MHz/256 = 328 kHz
|
|
|
+ // during initialization.
|
|
|
+ //
|
|
|
+ // It exports the following I/O ports, address bits can be combined.
|
|
|
+ // The actual connection to the CPU bus shifts the addresses left
|
|
|
+ // by two so that dword accesses can be done.
|
|
|
+ //
|
|
|
+ // Write, A[1:0]:
|
|
|
+ // 00 - control register:
|
|
|
+ // [6:0] - speed divider (CPU_HZ/(2*(divider+1)))
|
|
|
+ // 7 - CS# active
|
|
|
+ // 8 - clear CRC registers
|
|
|
+ // 9 - select CRC register inputs (0 = input, 1 = output)
|
|
|
+ // 01 - load output shift register from the CPU
|
|
|
+ // 10 - start transaction without loading
|
|
|
+ // 11 - load output shift register and start transaction
|
|
|
+ //
|
|
|
+ // Note: the triggered bus transaction size is set by byte enables.
|
|
|
+ // The output latch should be written left-aligned (most significant
|
|
|
+ // bytes within a dword); the input latch right-aligned.
|
|
|
+ //
|
|
|
+ // On read, A[1:0]:
|
|
|
+ // 00 - control register
|
|
|
+ // 01 - read input latch
|
|
|
+ // 10 - read CRC7 (in D[7:1], D0 = 1)
|
|
|
+ // 11 - read CRC16
|
|
|
+ // ------------------------------------------------------------------------
|
|
|
+
|
|
|
+ reg [31:0] sd_shr_out;
|
|
|
+ reg [31:0] sd_shr_in;
|
|
|
+ reg [4:0] sd_out_ctr; // Output bit counter
|
|
|
+ reg sd_active; // Transfer in progress
|
|
|
+ reg sd_active_neg; // Transfer in progress, first pos clock seen
|
|
|
+ reg sd_crcsrc; // CRC generator input
|
|
|
+ reg sd_crcstb; // Strobe for CRC generator
|
|
|
+ reg sd_cs_reg; // CS# active (positive logic, so inverted)
|
|
|
+ reg [6:0] sd_crc7; // CRC-7 generator
|
|
|
+ reg [15:0] sd_crc16; // CRC-16 generator
|
|
|
+ wire sd_cmd = valid & ~sd_active; // CPU command we can act on
|
|
|
+ reg sd_cmd_ok; // Valid CPU command received
|
|
|
+ wire sd_data_out = sd_shr_out[31];
|
|
|
+ reg sd_clk_out; // Output clock signal
|
|
|
+
|
|
|
+ // Output pins - tristate if card not present
|
|
|
+ assign sd_di = ~sd_cd_n ? sd_data_out : 1'bz;
|
|
|
+ assign sd_sclk = ~sd_cd_n ? sd_clk_out : 1'bz;
|
|
|
+ assign sd_cs_n = ~sd_cd_n ? ~sd_cs_reg : 1'bz;
|
|
|
+ assign sd_do = 1'bz; // Always an input
|
|
|
+
|
|
|
+ // If we try an action while a bus transaction is in progress,
|
|
|
+ // wait. The register sd_cmd_ok is used to prevent WAIT# from
|
|
|
+ // being asserted when we already started a transaction on *this*
|
|
|
+ // I/O operation.
|
|
|
+ //
|
|
|
+ // valid: 0 0 0 0 1 1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0
|
|
|
+ // sd_active: 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1
|
|
|
+ // sd_cmd: 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
|
|
|
+ // sd_cmd_ok: 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0
|
|
|
+ // cpu_wait_n: 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1
|
|
|
+
|
|
|
+ always @(negedge rst_n or posedge clk)
|
|
|
+ if (~rst_n)
|
|
|
+ sd_cmd_ok <= 1'b0;
|
|
|
+ else
|
|
|
+ sd_cmd_ok <= valid & (~sd_active | sd_cmd_ok);
|
|
|
+
|
|
|
+ assign wait_n = ~(valid & sd_active) | sd_cmd_ok;
|
|
|
+
|
|
|
+ // SD clock generator; this counter is used to generate the slow clock.
|
|
|
+ reg [6:0] sd_clk_div;
|
|
|
+ reg [6:0] sd_clk_ctr;
|
|
|
+ reg sd_clk_stb; // Clock strobe (clock flips next cycle)
|
|
|
+ reg sd_clk_pol; // Clock polarity
|
|
|
+ wire sd_clk_pos; // SD clock positive strobe
|
|
|
+ wire sd_clk_neg; // SD clock negative strobe
|
|
|
+
|
|
|
+ always @(posedge clk)
|
|
|
+ begin
|
|
|
+ if (|sd_clk_ctr)
|
|
|
+ begin
|
|
|
+ sd_clk_stb <= 1'b0;
|
|
|
+ sd_clk_ctr <= sd_clk_ctr - 1'b1;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ sd_clk_stb <= 1'b1;
|
|
|
+ sd_clk_pol <= ~sd_clk_pol; // Polarity of clock (one cycle early)
|
|
|
+ sd_clk_ctr <= sd_clk_div;
|
|
|
+ end
|
|
|
+ end // always @ (posedge clk)
|
|
|
+
|
|
|
+ // Generate strobes from the sd_clk_ctr; this is defined to be 1
|
|
|
+ assign sd_clk_pos = sd_active & sd_clk_stb & sd_clk_pol;
|
|
|
+ assign sd_clk_neg = sd_active_neg & sd_clk_stb & ~sd_clk_pol;
|
|
|
+
|
|
|
+ always @(negedge rst_n or posedge clk)
|
|
|
+ if (~rst_n)
|
|
|
+ sd_clk_out <= 1'b0;
|
|
|
+ else
|
|
|
+ sd_clk_out <= (sd_clk_out | sd_clk_pos) & ~sd_clk_neg;
|
|
|
+
|
|
|
+ wire [2:0] nwrite = wstrb[3] + wstrb[2] + wstrb[1] + wstrb[0];
|
|
|
+ wire rstrb = valid & ~|wstrb;
|
|
|
+
|
|
|
+ always @(negedge rst_n or posedge clk)
|
|
|
+ if (~rst_n)
|
|
|
+ begin
|
|
|
+ sd_shr_out <= 32'hffff_ffff;
|
|
|
+ sd_cs_reg <= 1'b0;
|
|
|
+ sd_clk_div <= 7'h7f;
|
|
|
+ sd_active <= 1'b0;
|
|
|
+ sd_active_neg <= 1'b0;
|
|
|
+ sd_out_ctr <= 5'h0;
|
|
|
+ sd_crcstb <= 1'b0;
|
|
|
+ sd_shr_in <= 32'hffff_ffff;
|
|
|
+ sd_crcsrc <= 1'b0;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ if (sd_clk_pos)
|
|
|
+ begin
|
|
|
+ sd_shr_in <= {sd_shr_in[30:0], sd_do};
|
|
|
+ sd_out_ctr <= sd_out_ctr + 1'b1;
|
|
|
+ sd_active_neg <= 1'b1;
|
|
|
+ end
|
|
|
+ if (sd_clk_neg)
|
|
|
+ begin
|
|
|
+ sd_shr_out <= {sd_shr_out[30:0], 1'b1};
|
|
|
+ sd_active <= |sd_out_ctr;
|
|
|
+ sd_active_neg <= |sd_out_ctr;
|
|
|
+ end
|
|
|
+ sd_crcstb <= sd_clk_pos; // CRCs are computed one cycle after posedge
|
|
|
+
|
|
|
+ if (sd_cmd)
|
|
|
+ begin
|
|
|
+ if (addr[1:0] == 2'b00)
|
|
|
+ begin
|
|
|
+ if (wstrb[0]) {sd_cs_reg, sd_clk_div} <= wdata[7:0];
|
|
|
+ if (wstrb[1]) sd_crcsrc <= wdata[9];
|
|
|
+ end
|
|
|
+
|
|
|
+ if (addr[0])
|
|
|
+ begin
|
|
|
+ if (wstrb[3]) sd_shr_out[31:24] <= wdata[31:24];
|
|
|
+ if (wstrb[2]) sd_shr_out[23:16] <= wdata[23:16];
|
|
|
+ if (wstrb[1]) sd_shr_out[15: 8] <= wdata[15: 8];
|
|
|
+ if (wstrb[0]) sd_shr_out[ 7: 0] <= wdata[ 7: 0];
|
|
|
+ end
|
|
|
+
|
|
|
+ if (addr[1])
|
|
|
+ begin
|
|
|
+ sd_active <= |wstrb;
|
|
|
+ sd_out_ctr <= { nwrite[1:0], 3'b000 };
|
|
|
+ end
|
|
|
+ end // if (sd_cmd)
|
|
|
+ end // else: !if(~rst_n)
|
|
|
+
|
|
|
+ wire clear_crc = ~rst_n |
|
|
|
+ (sd_cmd & (addr[1:0] == 2'b00) & wstrb[1] & wdata[8]);
|
|
|
+
|
|
|
+ // CRC generators: we have one 7-bit and one 16-bit, shared between
|
|
|
+ // input and output. The controller CPU has to specify where it wants
|
|
|
+ // the input from by setting A3 properly when starting a bus
|
|
|
+ // transaction (A4 = 1).
|
|
|
+ //
|
|
|
+ // The CRC generators run one cycle behind the positive sd_clk strobe.
|
|
|
+
|
|
|
+ wire sd_crcbit = sd_crcsrc ? sd_data_out : sd_shr_in[0];
|
|
|
+ wire sd_crc7in = sd_crcbit ^ sd_crc7[6];
|
|
|
+
|
|
|
+ always @(posedge clk)
|
|
|
+ if (clear_crc)
|
|
|
+ sd_crc7 <= 7'h00;
|
|
|
+ else if (sd_crcstb)
|
|
|
+ sd_crc7 <= {sd_crc7[5:3], sd_crc7[2]^sd_crc7in,
|
|
|
+ sd_crc7[1:0], sd_crc7in};
|
|
|
+
|
|
|
+ wire sd_crc16in = sd_crcbit ^ sd_crc16[15];
|
|
|
+
|
|
|
+ always @(posedge clk)
|
|
|
+ if (clear_crc)
|
|
|
+ sd_crc16 <= 16'h0000;
|
|
|
+ else if (sd_crcstb)
|
|
|
+ sd_crc16 <= {sd_crc16[14:12], sd_crc16[11]^sd_crc16in,
|
|
|
+ sd_crc16[10:5], sd_crc16[4]^sd_crc16in,
|
|
|
+ sd_crc16[3:0], sd_crc16in};
|
|
|
+
|
|
|
+ // Data out MUX
|
|
|
+ // Currently no registers with read side effects
|
|
|
+ always @(*)
|
|
|
+ begin
|
|
|
+ case (addr)
|
|
|
+ 2'b00: rdata = { 22'b0, sd_crcsrc, 1'b0, sd_cs_reg, sd_clk_div };
|
|
|
+ 2'b01: rdata = sd_shr_in;
|
|
|
+ 2'b10: rdata = { 24'b0, sd_crc7, 1'b1 };
|
|
|
+ 2'b11: rdata = { 16'b0, sd_crc16 };
|
|
|
+ endcase // case (addr)
|
|
|
+ end
|
|
|
+
|
|
|
+endmodule // sdcard
|