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
|
`timescale 1ns / 1ps
module spi_controller (
input wire clk,
input wire rst_n,
input wire [7:0] cmd_opcode,
input wire [31:0] cmd_address,
input wire [15:0] cmd_length,
input wire cmd_valid,
output wire cmd_ready,
input wire [7:0] data_in,
input wire data_in_valid,
output wire data_in_ready,
output wire [7:0] data_out,
output wire data_out_valid,
input wire data_out_ready,
output wire spi0_sck,
output wire spi0_mosi,
input wire spi0_miso,
output wire spi0_cs,
output wire spi1_sck,
output wire spi1_mosi,
input wire spi1_miso,
output wire spi1_cs,
output reg [3:0] flash_select,
output reg [3:0] spi_state,
output reg [3:0] status_reg
);
localparam STATE_IDLE = 4'h0;
localparam STATE_CMD = 4'h1;
localparam STATE_ADDR = 4'h2;
localparam STATE_DUMMY = 4'h3;
localparam STATE_DATA_RX = 4'h4;
localparam STATE_DATA_TX = 4'h5;
localparam STATE_WAIT = 4'h6;
localparam STATE_ERROR = 4'h7;
localparam MODE_STANDARD = 2'b00;
localparam MODE_FAST = 2'b01;
localparam MODE_DUAL = 2'b10;
localparam MODE_QUAD = 2'b11;
localparam CMD_WREN = 8'h06;
localparam CMD_WRDI = 8'h04;
localparam CMD_RDSR = 8'h05;
localparam CMD_WRSR = 8'h01;
localparam CMD_READ = 8'h03;
localparam CMD_FAST_READ = 8'h0B;
localparam CMD_DREAD = 8'h3B;
localparam CMD_QREAD = 8'h6B;
localparam CMD_PP = 8'h02;
localparam CMD_SE = 8'h20;
localparam CMD_BE32 = 8'h52;
localparam CMD_BE64 = 8'hD8;
localparam CMD_CE = 8'hC7;
localparam CMD_RDID = 8'h9F;
localparam CMD_RDFR = 8'h48;
localparam CMD_WRFR = 8'h42;
reg [7:0] spi_mode;
reg [2:0] spi_divider;
reg [31:0] current_addr;
reg [15:0] bytes_remaining;
reg [7:0] command_reg;
reg [3:0] state;
reg [3:0] next_state;
reg [7:0] tx_shift_reg;
reg [7:0] rx_shift_reg;
reg [3:0] bit_counter;
reg tx_active;
reg rx_active;
reg cs0_active;
reg cs1_active;
reg sck_active;
reg [23:0] delay_counter;
reg [7:0] clk_div_counter;
wire sck_posedge;
wire sck_negedge;
assign sck_posedge = (clk_div_counter == spi_divider);
assign sck_negedge = (clk_div_counter == (spi_divider >> 1));
assign spi0_sck = sck_active & cs0_active & ~spi0_cs;
assign spi1_sck = sck_active & cs1_active & ~spi1_cs;
assign spi0_cs = ~cs0_active;
assign spi1_cs = ~cs1_active;
assign spi0_mosi = tx_active ? tx_shift_reg[7] : 1'bz;
assign spi1_mosi = tx_active ? tx_shift_reg[7] : 1'bz;
assign cmd_ready = (state == STATE_IDLE);
assign data_in_ready = (state == STATE_DATA_TX) && tx_active && sck_negedge;
assign data_out_valid = (state == STATE_DATA_RX) && rx_active && sck_posedge;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= STATE_IDLE;
spi_state <= STATE_IDLE;
cs0_active <= 1'b0;
cs1_active <= 1'b0;
sck_active <= 1'b0;
tx_active <= 1'b0;
rx_active <= 1'b0;
spi_mode <= MODE_STANDARD;
spi_divider <= 3'd1;
flash_select <= 2'b00;
status_reg <= 4'b0000;
end else begin
state <= next_state;
spi_state <= state;
case (state)
STATE_IDLE: begin
cs0_active <= 1'b0;
cs1_active <= 1'b0;
sck_active <= 1'b0;
tx_active <= 1'b0;
rx_active <= 1'b0;
if (cmd_valid && cmd_ready) begin
if (cmd_address[24] == 1'b0) begin
cs0_active <= 1'b1;
flash_select <= 2'b01;
end else begin
cs1_active <= 1'b1;
flash_select <= 2'b10;
end
command_reg <= cmd_opcode;
current_addr <= cmd_address;
bytes_remaining <= cmd_length;
next_state <= STATE_CMD;
end
end
STATE_CMD: begin
if (sck_negedge) begin
if (bit_counter == 4'd7) begin
bit_counter <= 4'd0;
if (command_reg == CMD_READ ||
command_reg == CMD_FAST_READ ||
command_reg == CMD_PP) begin
next_state <= STATE_ADDR;
end else if (command_reg == CMD_RDSR ||
command_reg == CMD_RDID) begin
next_state <= STATE_DATA_RX;
end else begin
next_state <= STATE_WAIT;
end
end else begin
tx_shift_reg <= tx_shift_reg << 1;
bit_counter <= bit_counter + 1;
end
end
end
STATE_ADDR: begin
if (sck_negedge) begin
if (bit_counter == 4'd7) begin
bit_counter <= 4'd0;
if (addr_byte_counter == 3'd2) begin
if (command_reg == CMD_FAST_READ ||
command_reg == CMD_DREAD ||
command_reg == CMD_QREAD) begin
next_state <= STATE_DUMMY;
end else if (command_reg == CMD_READ) begin
next_state <= STATE_DATA_RX;
end else if (command_reg == CMD_PP) begin
next_state <= STATE_DATA_TX;
end else begin
next_state <= STATE_WAIT;
end
end else begin
addr_byte_counter <= addr_byte_counter + 1;
end
end else begin
tx_shift_reg <= tx_shift_reg << 1;
bit_counter <= bit_counter + 1;
end
end
end
STATE_DATA_RX: begin
rx_active <= 1'b1;
if (sck_posedge) begin
rx_shift_reg <= {rx_shift_reg[6:0], (cs0_active ? spi0_miso : spi1_miso)};
if (bit_counter == 4'd7) begin
bit_counter <= 4'd0;
data_out <= rx_shift_reg;
if (bytes_remaining > 0) begin
bytes_remaining <= bytes_remaining - 1;
end else begin
next_state <= STATE_IDLE;
rx_active <= 1'b0;
end
end else begin
bit_counter <= bit_counter + 1;
end
end
end
STATE_DATA_TX: begin
tx_active <= 1'b1;
if (sck_negedge) begin
if (bit_counter == 4'd7) begin
bit_counter <= 4'd0;
if (data_in_valid && data_in_ready) begin
tx_shift_reg <= data_in;
if (bytes_remaining > 0) begin
bytes_remaining <= bytes_remaining - 1;
end else begin
next_state <= STATE_WAIT;
tx_active <= 1'b0;
end
end
end else begin
tx_shift_reg <= tx_shift_reg << 1;
bit_counter <= bit_counter + 1;
end
end
end
STATE_WAIT: begin
if (delay_counter == 24'hFFFFFF) begin
next_state <= STATE_IDLE;
end else begin
delay_counter <= delay_counter + 1;
end
end
STATE_ERROR: begin
cs0_active <= 1'b0;
cs1_active <= 1'b0;
sck_active <= 1'b0;
next_state <= STATE_IDLE;
end
endcase
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
clk_div_counter <= 8'd0;
sck_active <= 1'b0;
end else begin
if (cs0_active || cs1_active) begin
if (clk_div_counter >= spi_divider) begin
clk_div_counter <= 8'd0;
sck_active <= ~sck_active;
end else begin
clk_div_counter <= clk_div_counter + 1;
end
end else begin
clk_div_counter <= 8'd0;
sck_active <= 1'b0;
end
end
end
reg [2:0] addr_byte_counter;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
addr_byte_counter <= 3'd0;
end else if (state == STATE_ADDR) begin
end else begin
addr_byte_counter <= 3'd0;
end
end
initial begin
tx_shift_reg = 8'h00;
rx_shift_reg = 8'h00;
bit_counter = 4'd0;
delay_counter = 24'h0;
end
endmodule
|