|
@@ -16,24 +16,35 @@
|
|
|
// This runs the SD card in SPI mode. In the future, consider improving
|
|
|
// performance by switching to quad SD mode.
|
|
|
//
|
|
|
+// Note: this is also usable as generic SPI master in many cases.
|
|
|
|
|
|
-module sdcard (
|
|
|
- input rst_n, // Global reset
|
|
|
- input clk, // System clock (84 MHz)
|
|
|
+module sdcard
|
|
|
+ #(
|
|
|
+ parameter [0:0] with_crc7 = 1'b1,
|
|
|
+ parameter [6:0] crc7_poly = 7'b000_1001,
|
|
|
+ parameter [0:0] with_crc16 = 1'b1,
|
|
|
+ parameter [15:0] crc16_poly = 16'b0001_0000_0010_0001,
|
|
|
+ parameter [7:0] with_irq_mask = 8'b0000_0000
|
|
|
+ )
|
|
|
+ (
|
|
|
+ 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
|
|
|
+ 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 sd_irq_n, // External IRQ input (optional)
|
|
|
|
|
|
- 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
|
|
|
- );
|
|
|
+ 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
|
|
|
+ output irq // CPU interrupt request
|
|
|
+ );
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
// SD card interface
|
|
@@ -48,10 +59,12 @@ module sdcard (
|
|
|
//
|
|
|
// 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
|
|
|
+ // [6:0] - speed divider (CPU_HZ/(2*(divider+1)))
|
|
|
+ // [7] - CS# active
|
|
|
+ // [15:8] - IRQ enable mask
|
|
|
+ // [22] - clear read CRC
|
|
|
+ // [23] - clear write CRC
|
|
|
+ //
|
|
|
// x0xxx - reserved
|
|
|
// x1e00 - load shift register but don't start transaction
|
|
|
// x1e01 - load shift register and start transaction, 8 bits
|
|
@@ -65,10 +78,12 @@ module sdcard (
|
|
|
// bytes within a dword); the input latch right-aligned.
|
|
|
//
|
|
|
// Read:
|
|
|
- // 00000 - control register
|
|
|
- // 00010 - [7:0] - read CRC7 + final 1 bit
|
|
|
+ // 00000 - [15:0] - control register
|
|
|
+ // [16] - busy status
|
|
|
+ // [31:24] - IRQ status
|
|
|
+ // 00100 - [7:0] - read CRC7 + final 1 bit
|
|
|
// [31:16] - read CRC16
|
|
|
- // 00011 - [7:0] - write CRC7 + final 1 bit
|
|
|
+ // 00101 - [7:0] - write CRC7 + final 1 bit
|
|
|
// [31:16] - write CRC16
|
|
|
// x0xxx - reserved
|
|
|
// x1e00 - read shift register but don't start transaction
|
|
@@ -77,6 +92,13 @@ module sdcard (
|
|
|
// x1e11 - read shift register and start transaction, 32 bits
|
|
|
// 11xxx - clear read CRC registers
|
|
|
//
|
|
|
+ // Addresses of the form 000xx are non-blocking; others stall the CPU
|
|
|
+ // until the current transaction is complete.
|
|
|
+ //
|
|
|
+ // Available interrupts are:
|
|
|
+ // 0 - unit idle
|
|
|
+ // 1 - card detect (sd_cd_n low)
|
|
|
+ // 2 - external interrupt (sd_irq_n low)
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
reg [31:0] sd_shr_out;
|
|
@@ -87,8 +109,6 @@ module sdcard (
|
|
|
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
|
|
|
|
|
@@ -107,14 +127,23 @@ module sdcard (
|
|
|
// 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
|
|
|
+ //
|
|
|
+
|
|
|
+ wire valid_blocking = valid & (addr[4:2] != 3'b000);
|
|
|
+ wire sd_cmd = valid_blocking & ~sd_active;
|
|
|
+ // CPU command we can act on
|
|
|
+ reg sd_cmd_ok; // Valid CPU command received
|
|
|
|
|
|
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);
|
|
|
+ sd_cmd_ok <= valid_blocking & (~sd_active | sd_cmd_ok);
|
|
|
|
|
|
- assign wait_n = ~(valid & sd_active) | sd_cmd_ok;
|
|
|
+ assign wait_n = ~(valid_blocking & sd_active) | sd_cmd_ok;
|
|
|
+
|
|
|
+ // Valid *nonblocking* command
|
|
|
+ wire sd_cmd_nonblock = valid & (addr[4:2] == 3'b000);
|
|
|
|
|
|
// SD clock generator; this counter is used to generate the slow clock.
|
|
|
reg [6:0] sd_clk_div;
|
|
@@ -149,6 +178,25 @@ module sdcard (
|
|
|
else
|
|
|
sd_clk_out <= (sd_clk_out | sd_clk_pos) & ~sd_clk_neg;
|
|
|
|
|
|
+ // IRQ handling (extensible for future uses)
|
|
|
+ reg [7:0] irq_status;
|
|
|
+ always @(posedge clk)
|
|
|
+ begin
|
|
|
+ irq_status <= with_irq_mask &
|
|
|
+ {
|
|
|
+ 5'b0, // Reserved for future uses
|
|
|
+ ~sd_irq_n,
|
|
|
+ ~sd_cd_n,
|
|
|
+ ~sd_active
|
|
|
+ };
|
|
|
+ end
|
|
|
+ reg [7:0] irq_en;
|
|
|
+
|
|
|
+ assign irq = |(irq_status & irq_en & with_irq_mask);
|
|
|
+
|
|
|
+ //
|
|
|
+ // Main shift register state machine
|
|
|
+ //
|
|
|
reg [1:0] clear_crc;
|
|
|
|
|
|
always @(negedge rst_n or posedge clk)
|
|
@@ -164,6 +212,7 @@ module sdcard (
|
|
|
sd_shr_in <= 32'hffff_ffff;
|
|
|
sd_shr_in_q <= 32'hffff_ffff;
|
|
|
clear_crc <= 2'b11;
|
|
|
+ irq_en <= 8'b0;
|
|
|
end
|
|
|
else
|
|
|
begin
|
|
@@ -185,11 +234,23 @@ module sdcard (
|
|
|
clear_crc <= 2'b00; // No clearing by default
|
|
|
sd_crcstb <= sd_clk_pos; // CRCs are computed one cycle after posedge
|
|
|
|
|
|
+ if (sd_cmd_nonblock)
|
|
|
+ casez(addr[1:0])
|
|
|
+ 2'b00: begin
|
|
|
+ if (wstrb[0]) {sd_cs_reg, sd_clk_div} <= wdata[7:0];
|
|
|
+ if (wstrb[1]) irq_en <= wdata[15:8] & with_irq_mask;
|
|
|
+ if (wstrb[2]) clear_crc <= wdata[23:22];
|
|
|
+ end
|
|
|
+ default: begin
|
|
|
+ // Do nothing
|
|
|
+ end
|
|
|
+ endcase
|
|
|
+
|
|
|
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
|
|
@@ -205,10 +266,6 @@ module sdcard (
|
|
|
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
|
|
@@ -251,39 +308,62 @@ module sdcard (
|
|
|
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)
|
|
|
+ always @(negedge rst_n or posedge clk)
|
|
|
+ if (~rst_n)
|
|
|
+ for (int i = 0; i < 2; i = i+1)
|
|
|
+ begin
|
|
|
+ sd_crc7[i] <= 7'hxx;
|
|
|
+ sd_crc16[i] <= 16'hxxxx;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ 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
|
|
|
+ if (with_crc7)
|
|
|
+ sd_crc7[i] <= { sd_crc7[i][5:0], 1'b0 }
|
|
|
+ ^ ({7{sd_crcbit[i] ^ sd_crc7[i][6]}}
|
|
|
+ & crc7_poly);
|
|
|
+ if (with_crc16)
|
|
|
+ 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 @(*)
|
|
|
+ always_comb
|
|
|
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;
|
|
|
+ 5'b0_0000: begin
|
|
|
+ rdata[31:16] = { irq_status, 7'b0, sd_active };
|
|
|
+ rdata[15: 0] = { irq_en, sd_cs_reg, sd_clk_div };
|
|
|
+ end
|
|
|
+ 5'b0_0100: begin
|
|
|
+ rdata[31:16] = with_crc16 ? sd_crc16[0] : 16'b0;
|
|
|
+ rdata[15: 8] = 8'b0;
|
|
|
+ rdata[ 7: 0] = with_crc7 ? { sd_crc7[0], 1'b1 } : 8'b0;
|
|
|
+ end
|
|
|
+ 5'b0_0101: begin
|
|
|
+ rdata[31:16] = with_crc16 ? sd_crc16[1] : 16'b0;
|
|
|
+ rdata[15: 8] = 8'b0;
|
|
|
+ rdata[ 7: 0] = with_crc7 ? { sd_crc7[1], 1'b1 } : 8'b0;
|
|
|
+ end
|
|
|
+ 5'b?_10??: begin
|
|
|
+ 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] };
|
|
|
+ end
|
|
|
+ 5'b?_11??: begin
|
|
|
+ rdata = sd_shr_in_q;
|
|
|
+ end
|
|
|
+ default: begin
|
|
|
+ rdata = 32'hxxxx_xxxx;
|
|
|
+ end
|
|
|
endcase
|
|
|
end
|
|
|
|