-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathspiCommMaster.v
325 lines (290 loc) · 13.4 KB
/
spiCommMaster.v
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
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
`timescale 1ns / 1ps
/* All files are owned by Kris Kalavantavanich.
* Feel free to use/modify/distribute in the condition that this copyright header is kept unmodified.
* Github: https://github.com/kkalavantavanich/SD2017 */
//////////////////////////////////////////////////////////////////////////////////
// Creator: Kris Kalavantavanich ([email protected])
// Create Date: 05/22/2017 08:17:18 PM
// Design Name: SPI Communication Master
// Module Name: spiCommMaster
// Project Name: SD_2017
// Target Devices: Basys3
// Description: Communication Master manages Command and Response loop.
// Doesn't do data receive/send separately (managed by main module).
// Dependencies: main.v
// Revision: 1.02
// Revision 1.02 - Debug
// Revision 1.00 - Finished Read Operation
// Revision 0.01 - File Created
// Additional Comments:
// - Doesn't interpret response -- (Master of this)'s job.
// - All signals are synchronous (including reset). Reset without clock means nothing.
// --- Except enable signal
//////////////////////////////////////////////////////////////////////////////////
module spiCommMaster(
input cpuClock, // CPU Clock Input
input enable, // If LOW => all outputs will be Z (high impedence) (Active HIGH)
input reset, // IF HIGH => Reset to ST_IDLE (Active HIGH)
input spiClockEn, // Enable SPI Clock (SD_SDLK) (Active HIGH)
input spiClockBS, // 0 = normal, 1 = byte sync mode
output errorInterrupt, // HIGH <= Error has occurred
output [3:0] errorType, // output the type of error
output [3:0] errorState, // output the last error if an error occurs
input cmdTransmitBit, // Transmit bit of CMD (second MSB of CMD)
input [5:0] cmdIndex, // CMD Index
input [31:0] cmdArgument, // CMD Argument(s)
input [1:0] readMode, // Reading Mode (00 = readSingle, 01 = readDouble, 10 = readWide, 11 = reserved)
output [39:0] readResponse, // Response of Slave SPI Device. If response is less than 40-bits, response is aligned right and left-filled with 0.
input commStart, // (Signal) Start Communication Cycle from ST_IDLE. posedge = Start, negedge = Stop.
output commFinish, // (Signal) Finished Communication Cycle and waiting for Response to be fetched. HIGH only if ST_FINISH.
input SD_MISO, // SD Master In Slave Out
input SD_CD, // SD Chip Detected
input SD_WP, // SD Write Protect
output SD_SCLK, // SD SPI Clock (Controlled only with spiClockEn; not by enable)
output SD_MOSI, // SD Master Out Slave In
output SD_CS // SD Chip Select
);
// Common Acronyms //
// ST = State
// STA = Start
// STO = Stop
// EN = Enable (out value or high impedence)
// RST = Reset
// FIN = Finished
// OUT = wire data out
// BUFF = reg data
// DEFINE CONSTANTS //
localparam ST_IDLE = 4'b0000; // Wait for `commStart` HIGH
localparam ST_GEN_CRC = 4'b0010; // Wait for previous cooldown and start CRC Generation
localparam ST_SEND = 4'b0100; // Wait for CRC generation and start sending
localparam ST_READ = 4'b0110; // Wait for Sending finish and start reading
localparam ST_INPUT = 4'b1000; // Wait for Reading
localparam ST_COOLDOWN = 4'b1010; // Cooldown Requied (does nothing)
localparam ST_FINISH = 4'b1100; // Finish and wait for `commStart` LOW
localparam ST_ERROR = 4'b1111; // Error state
localparam ER_UNKN = 4'b0000; // No Error / Unknown Error
localparam ER_IVST = 4'b0001; // Invalid State Error
localparam ER_TMOT = 4'b0010; // Timeout Error
localparam ER_RESP = 4'b0100; // Response Error
localparam ER_UDEV = 4'b0110; // Unknown Device Error
// STATE //
reg [3:0] CST = ST_IDLE; // Current State
reg [3:0] NST = ST_IDLE; // Next State
// ERRORS //
reg EINT = 0; // Error Interrupt
reg EINTE = 1; // Error Interrupt Enable
reg [3:0] EST = 0; // Internal Error State (State before ST_ERROR)
reg [3:0] ETYPE = 0; // Error Type
assign errorInterrupt = enable ? (EINTE ? EINT : 1'b0) : 1'bZ;
assign errorType = enable ? ETYPE : 4'bZ;
assign errorState = enable ? EST : 4'bZ;
// BUFFERED ENABLE OUTPUTS //
wire [39:0] readData; // Internal Read Response Wiring
assign readResponse = enable ? readData : 40'bZ;
wire MOSI; // MOSI Data
reg MOSI_EN = 0; // MOSI Enable (Active HIGH)
assign SD_MOSI = enable ? (MOSI_EN ? MOSI : 1'b1): 1'bZ;
reg CS = 1; // Chip Select (Active LOW)
assign SD_CS = enable ? CS : 1'bZ;
assign commFinish = (CST == 4'b1100);
// SPI CLOCK //
wire spiClock, _spiClock; // _spiClock is without enable, spiClock is with enable
clockDiv #(8) SPICLKM (cpuClock, _spiClock); // run _spiClock at 390.6 kHz
//clockDiv #(9) c4(cpuClock, _spiClock); // run _spiClock at 195.3 kHz
wire byte_sync;
assign spiClock = (spiClockEn && enable) ? byte_sync & _spiClock : 1; // spiClock is active low
assign SD_SCLK = (spiClockEn ? byte_sync & _spiClock : 1);
// SPI TIMEOUT TIMER (TMTO)//
localparam TMTO_bitSize = 16;
reg TMTO_RST = 1;
wire [TMTO_bitSize-1 :0] TMTO_OUT;
wire TMTO_OV;
wire [TMTO_bitSize-1 :0] TMTO_VAL; // Value for timeout
wire TMTO_TOI; // Timeout Interrupt
assign TMTO_TOI = (TMTO_OUT >= TMTO_VAL);
counter #(TMTO_bitSize) TMTOM (_spiClock, TMTO_RST, TMTO_OUT, TMTO_OV);
// SPI COOLDOWN TIMER (TMWC)//
localparam TMWC_bitSize = 4;
reg TMWC_RST = 1;
wire [TMWC_bitSize-1 :0] TMWC_OUT;
wire TMWC_OV; // set whether cooldown timer finished
wire TMWC_MSB; // set whether SD_CS is on
assign TMWC_MSB = TMWC_OUT[TMWC_bitSize - 1];
counter #(TMWC_bitSize) TMWCM (_spiClock, TMWC_RST, TMWC_OUT, TMWC_OV);
// SPI BYTE SYNC TIMER (TMBS) //
// every 9 bit 1 bit is down
localparam TMBS_bitSize = 4;
reg TMBS_RST = 1;
wire [TMBS_bitSize-1 :0] TMBS_OUT;
wire TMBS_OV; // set whether cooldown timer finished
wire TMBS_MSB; // set whether SD_CS is on
counter #(TMBS_bitSize, 8) TMBSM (_spiClock, TMBS_RST, TMBS_OUT, TMBS_OV);
wire TMBS_TOGGLE;
assign TMBS_TOGGLE = TMBS_OUT[3];
assign byte_sync = spiClockBS ? ~TMBS_TOGGLE : 1'b1;
reg [1:0] spiClockBS_BUFF = 0;
always @ (negedge _spiClock) begin
if ({spiClockBS_BUFF, spiClockBS} == 2'b01) begin // posedge
TMBS_RST = 0;
end else if ({spiClockBS_BUFF, spiClockBS} == 2'b10) begin // negedge
TMBS_RST = 1;
end
spiClockBS_BUFF <= {spiClockBS_BUFF, spiClockBS};
end
// COMMANDS //
// Constructed from (Inputs to Module)
wire [5:0] CMD_INDEX; // Command Index (CMD0 - CMD63)
wire [31:0] CMD_ARG; // 32-bit Command Argument
wire [6:0] CMD_CRC; // CRC-7 Code
wire CMD_TRANSMIT; // 0 => Receiver, 1 => Transmitter
wire [47:0] CMD; // 48-bit command
assign CMD_INDEX = cmdIndex;
assign CMD_ARG = cmdArgument;
assign CMD_TRANSMIT = cmdTransmitBit;
assign CMD = {1'b0, CMD_TRANSMIT, CMD_INDEX, CMD_ARG, CMD_CRC, 1'b1};
// SPI SEND (SPS) //
reg SPS_STA = 0;
reg [47:0] SPS_BUFF;
wire SPS_FIN;
spiSend SPSM (spiClock, SPS_STA, SPS_BUFF, MOSI, SPS_FIN);
// SPI READ (SPR) //
// Normal Response (Single Byte) (SPRS)
reg SPRS_STA = 0; // SPRSM Start
wire [7:0] SPRS_OUT; // Output from SPRSM (Can be high impedence)
wire SPRS_FIN; // SPRSM Finish
spiRead SPRSM (spiClock, SPRS_STA, SD_MISO, SPRS_FIN, SPRS_OUT, 1'b1);
// Double Byte Response (Double Byte) (SPRD)
reg SPRD_STA = 0;
wire [15:0] SPRD_OUT;
wire SPRD_FIN;
spiRead #(2) SPRDM (spiClock, SPRD_STA, SD_MISO, SPRD_FIN, SPRD_OUT, 1'b1);
// Wide Response (R3 / R7) -- 5 bytes (R1 + Rn) (SPRW)
reg SPRW_STA = 0;
wire [39:0] SPRW_OUT;
wire SPRW_FIN;
spiRead #(5) SPRWM (spiClock, SPRW_STA, SD_MISO, SPRW_FIN, SPRW_OUT, 1'b1);
// SPI Reading -- Common //
wire SPR_FIN;
assign SPR_FIN = (readMode == 2'b00 ? SPRS_FIN : (readMode == 2'b01 ? SPRD_FIN : SPRW_FIN));
reg [39:0] SPR_BUFF;
assign readData = SPR_BUFF;
assign TMTO_VAL = (readMode == 2'b00 ? 100 : (readMode == 2'b01 ? 200 : 500));
// 'Response' CRC-7 (CRCR) //
// uses CPU CLOCK
wire [39:0] CRCR_IN;
reg CRCR_EN = 0;
wire CRCR_FIN;
wire [6:0] CRCR_OUT;
wire [2:0] CRCR_ST;
crcGenMaster CRCRM (cpuClock, CRCR_EN, CRCR_IN, 8'b10001001, CRCR_OUT, CRCR_FIN, CRCR_ST);
assign CRCR_IN = {1'b0, CMD_TRANSMIT, CMD_INDEX, CMD_ARG};
assign CMD_CRC = CRCR_OUT;
// MAIN STATE MACHINE //
always @ (negedge cpuClock) begin
if (reset) begin
NST <= ST_IDLE; // Set Next State to IDLE
EINT <= 0; // Clear Error Interrupt
ETYPE <= 0; // Clear Error Type
EST <= 0; // Clear Error State
MOSI_EN <= 0; // Disable MOSI
CS <= 1; // Disable Chip Select
TMTO_RST <= 1; // Stop Timeout Timer
TMWC_RST <= 1; // Stop Warmup/Cooldown Timer
SPS_STA <= 0; // Stop Sending (SPS)
SPS_BUFF <= 0; // Clear Sending Buffer
SPRS_STA <= 0; // Stop Reading Single
SPRD_STA <= 0; // Stop Reading Double
SPRW_STA <= 0; // Stop Reading Wide
SPR_BUFF <= 0; // Clear Reading Buffer
CRCR_EN <= 0; // Disable CRC-7 Generation
end else begin
case (CST)
ST_IDLE: begin
if (commStart) begin
NST <= ST_GEN_CRC;
end
end
ST_GEN_CRC: begin // Start CRC generation if previous cooldown finished. CMD data *must* be stable and complete.
if (!TMWC_OV) begin
CS <= 1; // CS HIGH before warmup
TMWC_RST <= 0; // Start warmup/cooldown timer
CRCR_EN <= 1; // Start CRC7 generation
NST <= ST_SEND; // goto 'send' state
end
end
ST_SEND: begin // Wait for CRC generation/warmup and start sending.
if (CRCR_FIN && TMWC_OV) begin
TMWC_RST <= 1; // Stop warmup/cooldown timer
CS <= 0; // Chip Select Must be LOW
SPS_STA <= 1; // Start Sending
SPS_BUFF <= CMD; // Copy CMD to SPS Internal Buffer
MOSI_EN <= 1; // Enable MOSI
CRCR_EN <= 0; // Stop CRC7 generation
NST <= ST_READ; // goto 'read' state
end else begin
CS <= ~TMWC_MSB; // Set CS to be half HIGH, half LOW for TMWC duration
end
end
ST_READ: begin // Wait for sending and start reading.
if (SPS_FIN) begin
SPS_STA <= 0; // Stop Sending
MOSI_EN <= 0; // Disable MOSI
TMTO_RST <= 0; // Start timeout timer (TMTO)
case (readMode) // Start Reader
2'b00: SPRS_STA <= 1;
2'b01: SPRD_STA <= 1;
default: SPRW_STA <= 1;
endcase
NST <= ST_INPUT;
end
end
ST_INPUT: begin // Wait for Reading
if (TMTO_TOI) begin
NST <= ST_ERROR;
ETYPE <= ER_TMOT; // Timeout Error
EINT <= 1; // Raise Error Interrupt
EST <= CST; // Store Error State
end else if (SPR_FIN) begin
TMTO_RST <= 1; // Stop Timeout Timer
TMWC_RST <= 0; // Start Cooldown Timer
case (readMode) // Copy data out to buffer
2'b00: SPR_BUFF <= {32'b0, SPRS_OUT};
2'b01: SPR_BUFF <= {24'b0, SPRD_OUT};
default: SPR_BUFF <= SPRW_OUT;
endcase
case (readMode) // Stop Readers
2'b00: SPRS_STA <= 0;
2'b01: SPRD_STA <= 0;
default: SPRW_STA <= 0;
endcase
NST <= ST_COOLDOWN; // goto `cooldown` state
end
end
ST_COOLDOWN: begin
if (TMWC_OV) begin
TMWC_RST <= 1; // Stop Cooldown Timer
CS <= 1; // CS HIGH After cooldown
NST <= ST_FINISH; // goto `finish` state
end else begin
CS <= TMWC_MSB; // Set value of CS to be half LOW, half HIGH during TMWC duration
end
end
ST_FINISH: begin // Finished Comm Cycle State. Waiting for start LOW
if (!commStart) begin
NST <= ST_IDLE;
end
end
ST_ERROR: begin // Error State. Will stay in this state until `reset` HIGH
end
default: begin
NST <= ST_ERROR;
ETYPE <= ER_IVST;
EST <= 0;
end
endcase
end
end // MAIN STATE MACHINE//
always @ (posedge cpuClock) begin
CST <= NST;
end
endmodule