// ----------------------------------------------------------------------- // // 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) output sd_cs_n, // SD card CS# (CD, DAT3) output sd_di, // SD card DI (MOSI, CMD) output sd_sclk, // SD card CLK (SCLK) input sd_do, // SD card SO (MISO, DAT0) input sd_cd_n, // Card detect 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 [4: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: // 00000 - control register: // [6:0] - speed divider (CPU_HZ/(2*(divider+1))) // 7 - CS# active // 8 - clear read CRC registers // 9 - clear write CRC registers // x0xxx - reserved // x1e00 - load shift register but don't start transaction // x1e01 - load shift register and start transaction, 8 bits // x1e10 - load shift register and start transaction, 16 bits // x1e11 - load shift register and start transaction, 32 bits // 11xxx - clear write CRC registers // e = endian; 0 = wire byte order; 1 = bigendian byte order // // 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. // // Read: // 00000 - control register // 00010 - [7:0] - read CRC7 + final 1 bit // [31:16] - read CRC16 // 00011 - [7:0] - write CRC7 + final 1 bit // [31:16] - write CRC16 // x0xxx - reserved // x1e00 - read shift register but don't start transaction // x1e01 - read shift register and start transaction, 8 bits // x1e10 - read shift register and start transaction, 16 bits // x1e11 - read shift register and start transaction, 32 bits // 11xxx - clear read CRC registers // // ------------------------------------------------------------------------ reg [31:0] sd_shr_out; reg [31:0] sd_shr_in; reg [31:0] sd_shr_in_q; 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_crcstb; // Strobe for CRC generator reg sd_cs_reg; // CS# active (positive logic, so inverted) 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; // 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; reg [1:0] clear_crc; 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_shr_in_q <= 32'hffff_ffff; clear_crc <= 2'b11; 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; if (~|sd_out_ctr) sd_shr_in_q <= sd_shr_in; end clear_crc <= 2'b00; // No clearing by default sd_crcstb <= sd_clk_pos; // CRCs are computed one cycle after posedge if (sd_cmd) begin if (addr[4:3] == 2'b11) clear_crc <= {|wstrb, ~|wstrb}; casez (addr) 5'b?10??: begin // Load in host (littleendian) byte order if (wstrb[3]) sd_shr_out[ 7: 0] <= wdata[31:24]; if (wstrb[2]) sd_shr_out[15: 8] <= wdata[23:16]; if (wstrb[1]) sd_shr_out[23:16] <= wdata[15: 8]; if (wstrb[0]) sd_shr_out[31:24] <= wdata[ 7: 0]; end 5'b?11??: begin // Load in SPI (bigendian) byte order 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 5'b00000: begin if (wstrb[0]) {sd_cs_reg, sd_clk_div} <= wdata[7:0]; if (wstrb[1]) clear_crc <= wdata[9:8]; end default: begin // do nothing end endcase // Begin transaction // Note: sd_out_ctr *increments* if (addr[3]) case (addr[1:0]) 2'b01: begin /* Start 8-bit transaction */ sd_active <= 1'b1; sd_out_ctr <= 5'b11_000; end 2'b10: begin /* Start 16-bit transaction */ sd_active <= 1'b1; sd_out_ctr <= 5'b10_000; end 2'b11: begin /* Start 32-bit transaction */ sd_active <= 1'b1; sd_out_ctr <= 5'b00_000; end default: begin // do nothing end endcase // case (addr[1:0]) end // if (sd_cmd) end // else: !if(~rst_n) // // CRC generators: we have two 7-bit and two 16-bit, one each for // input [0] and output [1]. // // The CRC generators run one cycle behind the positive sd_clk strobe. wire [1:0] sd_crcbit = { sd_data_out, sd_shr_in[0] }; reg [6:0] sd_crc7 [0:1]; // CRC-7 shift register reg [15:0] sd_crc16[0:1]; // CRC-16 shift register localparam [6:0] crc7_poly = 7'b000_1001; localparam [15:0] crc16_poly = 16'b0001_0000_0010_0001; always @(posedge clk) for (int i = 0; i < 2; i = i+1) begin if (clear_crc[i]) begin sd_crc7[i] <= 7'h00; sd_crc16[i] <= 16'h0000; end else if (sd_crcstb) begin sd_crc7[i] <= { sd_crc7[i][5:0], 1'b0 } ^ ({7{sd_crcbit[i] ^ sd_crc7[i][6]}} & crc7_poly); sd_crc16[i] <= { sd_crc16[i][14:0], 1'b0 } ^ ({16{sd_crcbit[i] ^ sd_crc16[i][15]}} & crc16_poly); end // else: !if(clear_crc[i]) end // for (int i = 0; i < 2; i = i+1) // Data out MUX always @(*) begin casez (addr) 5'b0_0000: rdata = { 24'b0, sd_cs_reg, sd_clk_div }; 5'b0_0010: rdata = { sd_crc16[0], 8'b0, sd_crc7[0], 1'b1 }; 5'b0_0011: rdata = { sd_crc16[1], 8'b0, sd_crc7[1], 1'b1 }; 5'b?_10??: rdata = { sd_shr_in_q[7:0], sd_shr_in_q[15:8], sd_shr_in_q[23:16], sd_shr_in_q[31:24] }; 5'b?_11??: rdata = sd_shr_in_q; default: rdata = 32'hxxxx_xxxx; endcase end endmodule // sdcard