summaryrefslogtreecommitdiffstats
path: root/sdcard.v
blob: d467c98418c5eb0c2785e9fe6765e6382f9a924a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
// -----------------------------------------------------------------------
//
//   Copyright 2003-2010 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 ABC8000
//
// Based on sddisk.v from the ABC80-DE1 project, but without an
// extra processor in the middle, and changed to be a 16-bit peripheral.
//

module sdcard (
	       input         rst_n,	// Global reset
	       input         clk,	// CPU clk (25 MHz)

	       inout         sd_cs_n,	// SD card CS# (CD, DAT3)
	       inout         sd_di,	// SD card DI (MOSI, CMD)
	       inout         sd_clk,	// 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  [15:0] cpu_do,	// CPU data out (CPU->controller)
	       output [15:0] cpu_di,	// CPU data in (controller->CPU)
	       input         msel,	// Select
	       input   [1:0] cpu_be_n,	// Byte enables
	       input   [5:0] cpu_a,	// Address bits
	       input         cpu_r_wn,	// R/W#
	       output        cpu_wait_n
	       );

   // ------------------------------------------------------------------------
   //  SD card interface
   //
   //  This drives the SD card in SPI mode.  We support two speeds:
   //  12.5 MHz for normal operation, and 25 MHz/64 = 391 kHz for
   //  initialization.
   // 
   //  It exports the following I/O ports, address bits can be combined.
   //  The actual connection to the 68000 bus shifts the addresses left
   //  by *two* so that dword accesses can be done (SD is bigendian.)
   //
   //  A0 write - set speed (D0 = low speed) and CS# (D1, 1 = not selected)
   //  A1 write - loads the output shift register from the CPU
   //  A2       - clear CRC registers
   //  A3       - select CRC register input (0 = input, 1 = output)
   //  A4       - bus transaction is 8 bits (default 16 bits)
   //  A5       - start bus transaction
   //
   //  On read, A[1:0]:
   //  00       - read input latch
   //  01       - read status: (WE#, CD#, CS#, slow)
   //  10       - read CRC7 (in D[7:1], D0 = 1)
   //  11       - read CRC16
   // ------------------------------------------------------------------------
   
   reg [15:0] sd_shr_out;
   reg [15:0] sd_shr_in;
   reg [3: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_cs_n_reg;	// CS# output
   reg 	      sd_slow;
   reg [5:0]  sd_clk_ctr;	// Counter for the clock
   reg 	      sd_clk_out;
   wire       sd_clk_pos;	// SD clock positive strobe
   wire       sd_clk_neg;	// SD clock negative strobe
   reg        sd_crcsrc;	// CRC generator input
   reg 	      sd_crcstb;	// Strobe for CRC generator
   reg [6:0]  sd_crc7;		// CRC-7 generator
   reg [15:0] sd_crc16;		// CRC-16 generator
   wire       sd_sel = msel & ~&cpu_be_n;   // Byte enables come late!
   wire       sd_cmd = sd_sel & ~sd_active; // CPU command we can act on
   reg 	      sd_cmd_ok;	// Valid CPU command received
   wire       sd_start = sd_cmd & cpu_a[5]; // Transaction begin
   wire       sd_data_out = sd_shr_out[15];
   
   // Output pins - tristate if card not present
   assign     sd_di   = ~sd_cd_n ? sd_data_out : 1'bz;
   assign     sd_clk  = ~sd_cd_n ? sd_clk_out  : 1'bz;
   assign     sd_cs_n = ~sd_cd_n ? sd_cs_n_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.
   //
   // sd_sel:     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 <= sd_sel & (~sd_active | sd_cmd_ok);
   
   assign cpu_wait_n = ~(sd_sel & sd_active) | sd_cmd_ok;

   // SD clock generator; this counter is used to generate the slow clock.
   always @(posedge clk)
     if (sd_start)
       sd_clk_ctr <= 0;
     else
       sd_clk_ctr <= sd_clk_ctr + 1'b1;

   // Generate strobes from the sd_clk_ctr; this is defined to be 1
   // in the internal cycle before sd_clk goes positive/negative.
   wire      ctr_pol = sd_slow ? sd_clk_ctr[5] : sd_clk_ctr[0];
   wire      ctr_val = ~sd_slow | &sd_clk_ctr[4:0];
   
   assign sd_clk_pos = sd_active & ctr_val & ctr_pol;
   assign sd_clk_neg = sd_active_neg & ctr_val & ~ctr_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;
   
   always @(negedge rst_n or posedge clk)
     if (~rst_n)
       begin
	  sd_shr_out    <= 16'hffff;
	  sd_cs_n_reg   <= 1'b1;
	  sd_slow       <= 1'b1;
	  sd_active     <= 1'b0;
	  sd_active_neg <= 1'b0;
	  sd_out_ctr    <= 4'h0;
	  sd_crcstb     <= 1'b0;
	  sd_shr_in     <= 16'hxxxx;
	  sd_crcsrc     <= 1'bx;
       end
     else
       begin
	  if (sd_clk_pos)
	    begin
	       sd_shr_in     <= {sd_shr_in[14: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[14: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 (~cpu_r_wn & cpu_a[0] & ~cpu_be_n[0])
		 {sd_cs_n_reg, sd_slow} <= cpu_do[1:0];

	       if (~cpu_r_wn & cpu_a[1] & ~cpu_be_n[1])
		 sd_shr_out[15:8] <= cpu_do[15:8];
	       if (~cpu_r_wn & cpu_a[1] & ~cpu_be_n[0])
		 sd_shr_out[7:0]  <= cpu_do[7:0];

	       if (cpu_a[5])
		 begin
		    sd_active  <= 1'b1;
		    sd_out_ctr <= { cpu_a[4], 3'b000 };
		    sd_crcsrc  <= cpu_a[3];
		 end
	    end // if (sd_cmd)
       end

   // 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 (sd_cmd & cpu_a[2])
       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 (sd_cmd & cpu_a[2])
       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 to CPU
   reg [15:0] sd_cpu_rd;
   reg 	      rd_q;

   always @(negedge rst_n or posedge clk)
     if (~rst_n)
       begin
	  sd_cpu_rd <= 16'hxxxx;
	  rd_q      <= 1'b0;
       end	  
     else
       begin
	  if (msel & cpu_r_wn)
	    begin
	       if (~sd_active & ~rd_q)
		 begin
		    rd_q <= 1'b1;
		    		   
		    casex (cpu_a[1:0])
		      2'b00:
			sd_cpu_rd <= sd_shr_in;
		      2'b01:
			sd_cpu_rd <= {12'b0, ~sd_we_n, ~sd_cd_n,
				      sd_cs_n_reg, sd_slow};
		      2'b10:
			sd_cpu_rd <= {8'b0, sd_crc7, 1'b1};
		      2'b11:
			sd_cpu_rd <= sd_crc16;
		    endcase // casex(cpu_a[1:0])
		 end // if (~sd_active & ~rd_q)
	    end // if (msel & cpu_r_wn)
	  else
	    begin
	       sd_cpu_rd <= 16'hffff;
	       rd_q <= 1'b0;
	    end // else: !if(msel & cpu_r_wn)
       end // else: !if(~rst_n)

   assign cpu_di = sd_cpu_rd;
   
endmodule // sdcard