@@ -41,8 +41,7 @@ def _format_cmd(self):
41
41
fcmd &= ~ (1 << (bit * self .spi_width ))
42
42
return fcmd
43
43
44
- def __init__ (self , * , protocol , data_width ,
45
- divisor = 1 , pins = None ):
44
+ def __init__ (self , * , protocol , data_width = 32 , divisor = 1 , divisor_bits = None , pins = None ):
46
45
if protocol not in ["standard" , "dual" , "quad" ]:
47
46
raise ValueError ("Invalid SPI protocol {!r}; must be one of "
48
47
"\" standard\" , \" dual\" , or \" quad\" "
@@ -53,8 +52,7 @@ def __init__(self, *, protocol, data_width,
53
52
if divisor < 1 :
54
53
raise ValueError ("Invalid divisor value; must be at least 1"
55
54
.format (divisor ))
56
- self .divisor = Signal (bits_for (divisor ), reset = divisor )
57
- self ._divisor_val = divisor + 1
55
+ self .divisor = Signal (divisor_bits or bits_for (divisor ), reset = divisor )
58
56
59
57
self ._pins = pins
60
58
@@ -87,6 +85,8 @@ def __init__(self, *, protocol, data_width,
87
85
# (Note: DQ pins are both input/output only for Dual or Quad SPI protocols)
88
86
self .shreg = Signal (max (self ._fcmd_width , self ._data_width ))
89
87
self .counter = Signal .like (self .divisor )
88
+ self .clk_posedge_next = Signal ()
89
+ self .clk_negedge_next = Signal ()
90
90
91
91
def _add_spi_hardware_logic (self , platform , module ):
92
92
"""Add the foundamental hardware logic for controlling all SPI pins to be used
@@ -108,12 +108,12 @@ def _add_spi_hardware_logic(self, platform, module):
108
108
module .submodules += FFSynchronizer (self ._pins .miso .i , self .miso )
109
109
module .d .comb += self ._pins .mosi .o .eq (self .mosi )
110
110
elif self ._protocol in ["dual" , "quad" ]:
111
- dq_oe = Signal ()
111
+ self . dq_oe = Signal ()
112
112
module .submodules .dq = platform .get_tristate (self .dq , self ._pins .dq , None , False )
113
113
# If the user doesn't give pins, create dq Pins for Dual & Quad
114
114
else :
115
115
if self ._protocol in ["dual" , "quad" ]:
116
- dq_oe = Signal ()
116
+ self . dq_oe = Signal ()
117
117
if self ._protocol == "dual" :
118
118
dq_pins = Record ([
119
119
("dq" , Pin (width = 2 , dir = "io" , xdr = 0 ).layout )
@@ -126,7 +126,7 @@ def _add_spi_hardware_logic(self, platform, module):
126
126
tristate_submodule .submodules += Instance ("$tribuf" ,
127
127
p_WIDTH = self .dq .width ,
128
128
i_EN = self .dq .oe ,
129
- i_A = Platformodule . _invert_if ( False , self .dq .o ) ,
129
+ i_A = self .dq .o ,
130
130
o_Y = dq_pins
131
131
)
132
132
module .submodules .dq = tristate_submodule
@@ -139,8 +139,10 @@ def _add_spi_hardware_logic(self, platform, module):
139
139
dq_i = Signal (self .spi_width )
140
140
# When countdown is half-way done, clock edge goes up (positive);
141
141
# MISO starts latching bit/byte from slave
142
- with module .If ((counter == self ._divisor_val >> 1 ) & self .cs ):
142
+ with module .If ((counter == ( self .divisor + 1 ) >> 1 ) & self .cs ):
143
143
module .d .sync += self .clk .eq (1 )
144
+ # Indicate imminent posedge
145
+ module .d .comb += self .clk_posedge_next .eq (1 )
144
146
if self ._protocol == "standard" :
145
147
module .d .sync += dq_i .eq (self .miso )
146
148
elif self ._protocol in ["dual" , "quad" ]:
@@ -152,6 +154,8 @@ def _add_spi_hardware_logic(self, platform, module):
152
154
self .clk .eq (0 ),
153
155
counter .eq (self .divisor )
154
156
]
157
+ # Indicate imminent negedge
158
+ module .d .comb += self .clk_negedge_next .eq (1 )
155
159
# MOSI latches from shreg by "pushing" old data out from the left of shreg
156
160
module .d .sync += shreg .eq (Cat (dq_i , shreg [:- self .spi_width ]))
157
161
# Normal countdown
@@ -168,7 +172,7 @@ def _add_spi_hardware_logic(self, platform, module):
168
172
elif self ._protocol in ["dual" , "quad" ]:
169
173
module .d .comb += [
170
174
self .dq .o .eq (shreg [- self .spi_width :]),
171
- self .dq .oe .eq (dq_oe )
175
+ self .dq .oe .eq (self . dq_oe )
172
176
]
173
177
174
178
@@ -203,10 +207,10 @@ def elaborate(self, platform):
203
207
# FSM
204
208
with m .FSM () as fsm :
205
209
state_durations = {
206
- "SLOWREAD-CMD" : self ._divisor_val * ( self . cmd_width // self .spi_width ) ,
207
- "SLOWREAD-ADDR" : self ._divisor_val * ( self . _addr_width // self .spi_width ) ,
208
- "SLOWREAD-READ" : self ._divisor_val * ( self . _data_width // self .spi_width ) ,
209
- "SLOWREAD-RDYWAIT" : 1 + self . _divisor_val
210
+ "SLOWREAD-CMD" : self .cmd_width // self .spi_width ,
211
+ "SLOWREAD-ADDR" : self ._addr_width // self .spi_width ,
212
+ "SLOWREAD-READ" : self ._data_width // self .spi_width ,
213
+ "SLOWREAD-RDYWAIT" : 1
210
214
}
211
215
max_duration = max ([dur for state , dur in state_durations .items ()])
212
216
# A "count-up" counter for each state of the command
@@ -216,67 +220,70 @@ def elaborate(self, platform):
216
220
m .d .comb += self .rdy .eq (1 )
217
221
with m .If (self .ack ):
218
222
m .d .sync += [
219
- counter .eq (self ._divisor_val ),
223
+ self .cs .eq (1 ),
224
+ counter .eq (0 ),
220
225
self .clk .eq (0 )
221
226
]
222
227
m .next = "SLOWREAD-CS"
223
228
# State: Chip select
224
229
with m .State ("SLOWREAD-CS" ):
225
- with m .If (counter == self ._divisor_val >> 1 ):
230
+ with m .If (self .clk_negedge_next ):
226
231
m .d .sync += [
227
232
state_counter .eq (0 ),
228
233
shreg [- self .cmd_width :].eq (fcmd )
229
234
]
230
235
if self ._protocol in ["dual" , "quad" ]:
231
- m .d .sync += dq_oe .eq (1 )
236
+ m .d .sync += self . dq_oe .eq (1 )
232
237
m .next = "SLOWREAD-CMD"
233
238
# State: Command, MOSI
234
239
with m .State ("SLOWREAD-CMD" ):
235
- with m .If (state_counter == state_durations ["SLOWREAD-CMD" ] - 1 ):
240
+ with m .If ((state_counter == state_durations ["SLOWREAD-CMD" ] - 1 ) &
241
+ self .negedge_next ):
236
242
m .d .sync += [
237
243
state_counter .eq (0 ),
238
244
shreg [- self ._addr_width :].eq (byte_addr )
239
245
]
240
246
m .next = "SLOWREAD-ADDR"
241
- with m .Else ( ):
247
+ with m .Elif ( self . clk_negedge_next ):
242
248
m .d .sync += state_counter .eq (state_counter + 1 )
243
249
# State: Address, MOSI
244
250
with m .State ("SLOWREAD-ADDR" ):
245
- with m .If (state_counter == state_durations ["SLOWREAD-ADDR" ] - 1 ):
251
+ with m .If ((state_counter == state_durations ["SLOWREAD-ADDR" ] - 1 ) &
252
+ self .negedge_next ):
246
253
m .d .sync += state_counter .eq (0 )
247
254
if self ._protocol in ["dual" , "quad" ]:
248
- m .d .sync += dq_oe .eq (0 )
255
+ m .d .sync += self . dq_oe .eq (0 )
249
256
m .next = "SLOWREAD-READ"
250
- with m .Else ( ):
257
+ with m .Elif ( self . clk_negedge_next ):
251
258
m .d .sync += state_counter .eq (state_counter + 1 )
252
259
# State: Dummy cycles (waiting), and then Read (MISO)
253
260
with m .State ("SLOWREAD-READ" ):
254
- with m .If (state_counter == state_durations ["SLOWREAD-READ" ] - 1 ):
261
+ with m .If ((state_counter == state_durations ["SLOWREAD-READ" ] - 1 ) &
262
+ self .negedge_next ):
255
263
m .d .sync += [
256
264
state_counter .eq (0 ),
257
- self .r_rdy .eq (1 )
265
+ self .cs .eq (0 )
258
266
]
259
267
if self ._protocol in ["dual" , "quad" ]:
260
- m .d .sync += dq_oe .eq (0 )
268
+ m .d .sync += self . dq_oe .eq (0 )
261
269
m .next = "SLOWREAD-RDYWAIT"
262
- with m .Else ( ):
270
+ with m .Elif ( self . clk_negedge_next ):
263
271
m .d .sync += state_counter .eq (state_counter + 1 )
264
272
# State: Send r_rdy (for 1 clock period), and then Wait
265
273
with m .State ("SLOWREAD-RDYWAIT" ):
266
- with m .If (state_counter == 0 ):
267
- m .d .sync += self .r_rdy .eq (0 )
268
274
# Early check to skip 1 clock period of doing nothing
269
- with m .If (state_counter == state_durations ["SLOWREAD-RDYWAIT" ] - 2 ):
270
- m .d .sync += state_counter .eq (0 )
271
- m .next = "SLOWREAD-IDLE"
272
- with m .Else ():
275
+ with m .If ((state_counter == state_durations ["FASTREAD-RDYWAIT" ] - 1 ) &
276
+ self .clk_posedge_next ):
277
+ m .d .sync += [
278
+ self .cs .eq (0 ),
279
+ state_counter .eq (0 )
280
+ ]
281
+ m .next = "FASTREAD-IDLE"
282
+ with m .Elif (self .clk_negedge_next ):
273
283
m .d .sync += state_counter .eq (state_counter + 1 )
274
- # Enable/Disable CS
275
- with m .If (~ fsm .ongoing ("SLOWREAD-IDLE" ) &
276
- ~ fsm .ongoing ("SLOWREAD-RDYWAIT" )):
277
- m .d .comb += self .cs .eq (1 )
278
- with m .Else ():
279
- m .d .comb += self .cs .eq (0 )
284
+
285
+ with m .If (fsm .ongoing ("FASTREAD-RDYWAIT" )):
286
+ m .d .comb += self .r_rdy .eq (1 )
280
287
281
288
return m
282
289
@@ -313,11 +320,10 @@ def elaborate(self, platform):
313
320
# FSM
314
321
with m .FSM () as fsm :
315
322
state_durations = {
316
- "FASTREAD-CMD" : self ._divisor_val * (self .cmd_width // self .spi_width ),
317
- "FASTREAD-ADDR" : self ._divisor_val * (self ._addr_width // self .spi_width ),
318
- "FASTREAD-WAITREAD" : self ._divisor_val * (self ._dummy_cycles +
319
- self ._data_width // self .spi_width ),
320
- "FASTREAD-RDYWAIT" : 1 + self ._divisor_val
323
+ "FASTREAD-CMD" : self .cmd_width // self .spi_width ,
324
+ "FASTREAD-ADDR" : self ._addr_width // self .spi_width ,
325
+ "FASTREAD-WAITREAD" : self ._dummy_cycles + self ._data_width // self .spi_width ,
326
+ "FASTREAD-RDYWAIT" : 1
321
327
}
322
328
max_duration = max ([dur for state , dur in state_durations .items ()])
323
329
# A "count-up" counter for each state of the command
@@ -327,66 +333,66 @@ def elaborate(self, platform):
327
333
m .d .comb += self .rdy .eq (1 )
328
334
with m .If (self .ack ):
329
335
m .d .sync += [
330
- counter .eq (self ._divisor_val ),
336
+ self .cs .eq (1 ),
337
+ counter .eq (0 ),
331
338
self .clk .eq (0 )
332
339
]
333
340
m .next = "FASTREAD-CS"
334
341
# State: Chip select
335
342
with m .State ("FASTREAD-CS" ):
336
- with m .If (counter == self ._divisor_val >> 1 ):
343
+ with m .If (self .clk_negedge_next ):
337
344
m .d .sync += [
338
345
state_counter .eq (0 ),
339
346
shreg [- self .cmd_width :].eq (fcmd )
340
347
]
341
348
if self ._protocol in ["dual" , "quad" ]:
342
- m .d .sync += dq_oe .eq (1 )
349
+ m .d .sync += self . dq_oe .eq (1 )
343
350
m .next = "FASTREAD-CMD"
344
351
# State: Command, MOSI
345
352
with m .State ("FASTREAD-CMD" ):
346
- with m .If (state_counter == state_durations ["FASTREAD-CMD" ] - 1 ):
353
+ with m .If ((state_counter == state_durations ["FASTREAD-CMD" ] - 1 ) &
354
+ self .clk_negedge_next ):
347
355
m .d .sync += [
348
356
state_counter .eq (0 ),
349
357
shreg [- self ._addr_width :].eq (byte_addr )
350
358
]
351
359
m .next = "FASTREAD-ADDR"
352
- with m .Else ( ):
360
+ with m .Elif ( self . clk_negedge_next ):
353
361
m .d .sync += state_counter .eq (state_counter + 1 )
354
362
# State: Address, MOSI
355
363
with m .State ("FASTREAD-ADDR" ):
356
- with m .If (state_counter == state_durations ["FASTREAD-ADDR" ] - 1 ):
364
+ with m .If ((state_counter == state_durations ["FASTREAD-ADDR" ] - 1 ) &
365
+ self .clk_negedge_next ):
357
366
m .d .sync += state_counter .eq (0 )
358
367
if self ._protocol in ["dual" , "quad" ]:
359
- m .d .sync += dq_oe .eq (0 )
368
+ m .d .sync += self . dq_oe .eq (0 )
360
369
m .next = "FASTREAD-WAITREAD"
361
- with m .Else ( ):
370
+ with m .Elif ( self . clk_negedge_next ):
362
371
m .d .sync += state_counter .eq (state_counter + 1 )
363
372
# State: Dummy cycles (waiting), and then Read (MISO)
364
373
with m .State ("FASTREAD-WAITREAD" ):
365
- with m .If (state_counter == state_durations ["FASTREAD-WAITREAD" ] - 1 ):
366
- m .d .sync += [
367
- state_counter .eq (0 ),
368
- self .r_rdy .eq (1 )
369
- ]
374
+ with m .If ((state_counter == state_durations ["FASTREAD-WAITREAD" ] - 1 ) &
375
+ self .clk_negedge_next ):
376
+ m .d .sync += state_counter .eq (0 )
370
377
if self ._protocol in ["dual" , "quad" ]:
371
- m .d .sync += dq_oe .eq (0 )
378
+ m .d .sync += self . dq_oe .eq (0 )
372
379
m .next = "FASTREAD-RDYWAIT"
373
- with m .Else ( ):
380
+ with m .Elif ( self . clk_negedge_next ):
374
381
m .d .sync += state_counter .eq (state_counter + 1 )
375
382
# State: Send r_rdy (for 1 clock period), and then Wait
376
383
with m .State ("FASTREAD-RDYWAIT" ):
377
- with m .If (state_counter == 0 ):
378
- m .d .sync += self .r_rdy .eq (0 )
379
384
# Early check to skip 1 clock period of doing nothing
380
- with m .If (state_counter == state_durations ["FASTREAD-RDYWAIT" ] - 2 ):
381
- m .d .sync += state_counter .eq (0 )
385
+ with m .If ((state_counter == state_durations ["FASTREAD-RDYWAIT" ] - 1 ) &
386
+ self .clk_posedge_next ):
387
+ m .d .sync += [
388
+ self .cs .eq (0 ),
389
+ state_counter .eq (0 )
390
+ ]
382
391
m .next = "FASTREAD-IDLE"
383
- with m .Else ( ):
392
+ with m .Elif ( self . clk_negedge_next ):
384
393
m .d .sync += state_counter .eq (state_counter + 1 )
385
- # Enable/Disable CS
386
- with m .If (~ fsm .ongoing ("FASTREAD-IDLE" ) &
387
- ~ fsm .ongoing ("FASTREAD-RDYWAIT" )):
388
- m .d .comb += self .cs .eq (1 )
389
- with m .Else ():
390
- m .d .comb += self .cs .eq (0 )
394
+
395
+ with m .If (fsm .ongoing ("FASTREAD-RDYWAIT" )):
396
+ m .d .comb += self .r_rdy .eq (1 )
391
397
392
398
return m
0 commit comments