Skip to content

Commit e941997

Browse files
committed
spi: add divisor_bits; fix divisor logic & simulation
1 parent 259225c commit e941997

File tree

2 files changed

+72
-68
lines changed

2 files changed

+72
-68
lines changed

nmigen_stdio/spiflash.py

Lines changed: 61 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ def _format_cmd(self):
4141
fcmd &= ~(1 << (bit*self.spi_width))
4242
return fcmd
4343

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):
4645
if protocol not in ["standard", "dual", "quad"]:
4746
raise ValueError("Invalid SPI protocol {!r}; must be one of "
4847
"\"standard\", \"dual\", or \"quad\""
@@ -53,8 +52,7 @@ def __init__(self, *, protocol, data_width,
5352
if divisor < 1:
5453
raise ValueError("Invalid divisor value; must be at least 1"
5554
.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)
5856

5957
self._pins = pins
6058

@@ -87,6 +85,8 @@ def __init__(self, *, protocol, data_width,
8785
# (Note: DQ pins are both input/output only for Dual or Quad SPI protocols)
8886
self.shreg = Signal(max(self._fcmd_width, self._data_width))
8987
self.counter = Signal.like(self.divisor)
88+
self.clk_posedge_next = Signal()
89+
self.clk_negedge_next = Signal()
9090

9191
def _add_spi_hardware_logic(self, platform, module):
9292
"""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):
108108
module.submodules += FFSynchronizer(self._pins.miso.i, self.miso)
109109
module.d.comb += self._pins.mosi.o.eq(self.mosi)
110110
elif self._protocol in ["dual", "quad"]:
111-
dq_oe = Signal()
111+
self.dq_oe = Signal()
112112
module.submodules.dq = platform.get_tristate(self.dq, self._pins.dq, None, False)
113113
# If the user doesn't give pins, create dq Pins for Dual & Quad
114114
else:
115115
if self._protocol in ["dual", "quad"]:
116-
dq_oe = Signal()
116+
self.dq_oe = Signal()
117117
if self._protocol == "dual":
118118
dq_pins = Record([
119119
("dq", Pin(width=2, dir="io", xdr=0).layout)
@@ -126,7 +126,7 @@ def _add_spi_hardware_logic(self, platform, module):
126126
tristate_submodule.submodules += Instance("$tribuf",
127127
p_WIDTH=self.dq.width,
128128
i_EN=self.dq.oe,
129-
i_A=Platformodule._invert_if(False, self.dq.o),
129+
i_A=self.dq.o,
130130
o_Y=dq_pins
131131
)
132132
module.submodules.dq = tristate_submodule
@@ -139,8 +139,10 @@ def _add_spi_hardware_logic(self, platform, module):
139139
dq_i = Signal(self.spi_width)
140140
# When countdown is half-way done, clock edge goes up (positive);
141141
# 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):
143143
module.d.sync += self.clk.eq(1)
144+
# Indicate imminent posedge
145+
module.d.comb += self.clk_posedge_next.eq(1)
144146
if self._protocol == "standard":
145147
module.d.sync += dq_i.eq(self.miso)
146148
elif self._protocol in ["dual", "quad"]:
@@ -152,6 +154,8 @@ def _add_spi_hardware_logic(self, platform, module):
152154
self.clk.eq(0),
153155
counter.eq(self.divisor)
154156
]
157+
# Indicate imminent negedge
158+
module.d.comb += self.clk_negedge_next.eq(1)
155159
# MOSI latches from shreg by "pushing" old data out from the left of shreg
156160
module.d.sync += shreg.eq(Cat(dq_i, shreg[:-self.spi_width]))
157161
# Normal countdown
@@ -168,7 +172,7 @@ def _add_spi_hardware_logic(self, platform, module):
168172
elif self._protocol in ["dual", "quad"]:
169173
module.d.comb += [
170174
self.dq.o.eq(shreg[-self.spi_width:]),
171-
self.dq.oe.eq(dq_oe)
175+
self.dq.oe.eq(self.dq_oe)
172176
]
173177

174178

@@ -203,10 +207,10 @@ def elaborate(self, platform):
203207
# FSM
204208
with m.FSM() as fsm:
205209
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
210214
}
211215
max_duration = max([dur for state, dur in state_durations.items()])
212216
# A "count-up" counter for each state of the command
@@ -216,67 +220,65 @@ def elaborate(self, platform):
216220
m.d.comb += self.rdy.eq(1)
217221
with m.If(self.ack):
218222
m.d.sync += [
219-
counter.eq(self._divisor_val),
223+
counter.eq(0),
220224
self.clk.eq(0)
221225
]
222226
m.next = "SLOWREAD-CS"
223227
# State: Chip select
224228
with m.State("SLOWREAD-CS"):
225-
with m.If(counter == self._divisor_val >> 1):
229+
m.d.sync += self.cs.eq(1)
230+
with m.If(self.clk_negedge_next):
226231
m.d.sync += [
227232
state_counter.eq(0),
228233
shreg[-self.cmd_width:].eq(fcmd)
229234
]
230235
if self._protocol in ["dual", "quad"]:
231-
m.d.sync += dq_oe.eq(1)
236+
m.d.sync += self.dq_oe.eq(1)
232237
m.next = "SLOWREAD-CMD"
233238
# State: Command, MOSI
234239
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.clk_negedge_next):
236242
m.d.sync += [
237243
state_counter.eq(0),
238244
shreg[-self._addr_width:].eq(byte_addr)
239245
]
240246
m.next = "SLOWREAD-ADDR"
241-
with m.Else():
247+
with m.Elif(self.clk_negedge_next):
242248
m.d.sync += state_counter.eq(state_counter + 1)
243249
# State: Address, MOSI
244250
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.clk_negedge_next):
246253
m.d.sync += state_counter.eq(0)
247254
if self._protocol in ["dual", "quad"]:
248-
m.d.sync += dq_oe.eq(0)
255+
m.d.sync += self.dq_oe.eq(0)
249256
m.next = "SLOWREAD-READ"
250-
with m.Else():
257+
with m.Elif(self.clk_negedge_next):
251258
m.d.sync += state_counter.eq(state_counter + 1)
252259
# State: Dummy cycles (waiting), and then Read (MISO)
253260
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.clk_negedge_next):
255263
m.d.sync += [
256264
state_counter.eq(0),
257265
self.r_rdy.eq(1)
258266
]
259267
if self._protocol in ["dual", "quad"]:
260-
m.d.sync += dq_oe.eq(0)
268+
m.d.sync += self.dq_oe.eq(0)
261269
m.next = "SLOWREAD-RDYWAIT"
262-
with m.Else():
270+
with m.Elif(self.clk_negedge_next):
263271
m.d.sync += state_counter.eq(state_counter + 1)
264272
# State: Send r_rdy (for 1 clock period), and then Wait
265273
with m.State("SLOWREAD-RDYWAIT"):
266-
with m.If(state_counter == 0):
267-
m.d.sync += self.r_rdy.eq(0)
274+
m.d.sync += self.r_rdy.eq(0)
268275
# 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)
276+
with m.If((state_counter == state_durations["SLOWREAD-RDYWAIT"] - 1) &
277+
self.clk_posedge_next):
278+
m.d.sync += self.cs.eq(0)
271279
m.next = "SLOWREAD-IDLE"
272-
with m.Else():
280+
with m.Elif(self.clk_negedge_next):
273281
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)
280282

281283
return m
282284

@@ -313,11 +315,10 @@ def elaborate(self, platform):
313315
# FSM
314316
with m.FSM() as fsm:
315317
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
318+
"FASTREAD-CMD" : self.cmd_width//self.spi_width,
319+
"FASTREAD-ADDR" : self._addr_width//self.spi_width,
320+
"FASTREAD-WAITREAD": self._dummy_cycles+self._data_width//self.spi_width,
321+
"FASTREAD-RDYWAIT" : 1
321322
}
322323
max_duration = max([dur for state, dur in state_durations.items()])
323324
# A "count-up" counter for each state of the command
@@ -327,66 +328,64 @@ def elaborate(self, platform):
327328
m.d.comb += self.rdy.eq(1)
328329
with m.If(self.ack):
329330
m.d.sync += [
330-
counter.eq(self._divisor_val),
331+
counter.eq(0),
331332
self.clk.eq(0)
332333
]
333334
m.next = "FASTREAD-CS"
334335
# State: Chip select
335336
with m.State("FASTREAD-CS"):
336-
with m.If(counter == self._divisor_val >> 1):
337+
m.d.sync += self.cs.eq(1)
338+
with m.If(self.clk_negedge_next):
337339
m.d.sync += [
338340
state_counter.eq(0),
339341
shreg[-self.cmd_width:].eq(fcmd)
340342
]
341343
if self._protocol in ["dual", "quad"]:
342-
m.d.sync += dq_oe.eq(1)
344+
m.d.sync += self.dq_oe.eq(1)
343345
m.next = "FASTREAD-CMD"
344346
# State: Command, MOSI
345347
with m.State("FASTREAD-CMD"):
346-
with m.If(state_counter == state_durations["FASTREAD-CMD"] - 1):
348+
with m.If((state_counter == state_durations["FASTREAD-CMD"] - 1) &
349+
self.clk_negedge_next):
347350
m.d.sync += [
348351
state_counter.eq(0),
349352
shreg[-self._addr_width:].eq(byte_addr)
350353
]
351354
m.next = "FASTREAD-ADDR"
352-
with m.Else():
355+
with m.Elif(self.clk_negedge_next):
353356
m.d.sync += state_counter.eq(state_counter + 1)
354357
# State: Address, MOSI
355358
with m.State("FASTREAD-ADDR"):
356-
with m.If(state_counter == state_durations["FASTREAD-ADDR"] - 1):
359+
with m.If((state_counter == state_durations["FASTREAD-ADDR"] - 1) &
360+
self.clk_negedge_next):
357361
m.d.sync += state_counter.eq(0)
358362
if self._protocol in ["dual", "quad"]:
359-
m.d.sync += dq_oe.eq(0)
363+
m.d.sync += self.dq_oe.eq(0)
360364
m.next = "FASTREAD-WAITREAD"
361-
with m.Else():
365+
with m.Elif(self.clk_negedge_next):
362366
m.d.sync += state_counter.eq(state_counter + 1)
363367
# State: Dummy cycles (waiting), and then Read (MISO)
364368
with m.State("FASTREAD-WAITREAD"):
365-
with m.If(state_counter == state_durations["FASTREAD-WAITREAD"] - 1):
369+
with m.If((state_counter == state_durations["FASTREAD-WAITREAD"] - 1) &
370+
self.clk_negedge_next):
366371
m.d.sync += [
367372
state_counter.eq(0),
368373
self.r_rdy.eq(1)
369374
]
370375
if self._protocol in ["dual", "quad"]:
371-
m.d.sync += dq_oe.eq(0)
376+
m.d.sync += self.dq_oe.eq(0)
372377
m.next = "FASTREAD-RDYWAIT"
373-
with m.Else():
378+
with m.Elif(self.clk_negedge_next):
374379
m.d.sync += state_counter.eq(state_counter + 1)
375380
# State: Send r_rdy (for 1 clock period), and then Wait
376381
with m.State("FASTREAD-RDYWAIT"):
377-
with m.If(state_counter == 0):
378-
m.d.sync += self.r_rdy.eq(0)
382+
m.d.sync += self.r_rdy.eq(0)
379383
# 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)
384+
with m.If((state_counter == state_durations["FASTREAD-RDYWAIT"] - 1) &
385+
self.clk_posedge_next):
386+
m.d.sync += self.cs.eq(0)
382387
m.next = "FASTREAD-IDLE"
383-
with m.Else():
388+
with m.Elif(self.clk_negedge_next):
384389
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)
391390

392391
return m

nmigen_stdio/test/test_spiflash.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def simulation_test(dut, process):
1515

1616
class SPIFlashFastReadTestCase(unittest.TestCase):
1717
def divisor_period(self):
18-
for _ in range((yield self.dut.divisor) + 1):
18+
for _ in range((yield self.divisor) + 1):
1919
yield
2020

2121
class SuperNaiveFlash(Elaboratable):
@@ -47,7 +47,7 @@ def elaborate(self, platform):
4747
with m.State("INIT"):
4848
m.d.sync += data_sig.eq(self.data)
4949
with m.If(self.dut.cs):
50-
with m.If(self.dut.counter == self.dut._divisor_val >> 1):
50+
with m.If(self.dut.counter == self.dut.divisor >> 1):
5151
m.d.comb += recv_data.w_en.eq(1)
5252
with m.Else():
5353
m.d.comb += recv_data.w_en.eq(0)
@@ -102,10 +102,11 @@ def elaborate(self, platform):
102102
return m
103103

104104
def test_standard(self):
105+
self.divisor = 9
105106
self.dut = SPIFlashFastReader(protocol="standard",
106107
addr_width=24,
107108
data_width=32,
108-
divisor=49,
109+
divisor=self.divisor,
109110
dummy_cycles=15)
110111
self.flash = (SPIFlashFastReadTestCase.
111112
SuperNaiveFlash(dut=self.dut,
@@ -115,10 +116,11 @@ def test_standard(self):
115116
self.simple_test()
116117

117118
def test_dual(self):
119+
self.divisor = 9
118120
self.dut = SPIFlashFastReader(protocol="dual",
119121
addr_width=24,
120122
data_width=32,
121-
divisor=49,
123+
divisor=self.divisor,
122124
dummy_cycles=15)
123125
self.flash = (SPIFlashFastReadTestCase.
124126
SuperNaiveFlash(dut=self.dut,
@@ -128,10 +130,11 @@ def test_dual(self):
128130
self.simple_test()
129131

130132
def test_quad(self):
133+
self.divisor = 9
131134
self.dut = SPIFlashFastReader(protocol="quad",
132135
addr_width=24,
133136
data_width=32,
134-
divisor=49,
137+
divisor=self.divisor,
135138
dummy_cycles=15)
136139
self.flash = (SPIFlashFastReadTestCase.
137140
SuperNaiveFlash(dut=self.dut,
@@ -154,8 +157,10 @@ def process():
154157
yield # Wait until it enters RDYWAIT state
155158
self.assertEqual((yield self.dut.r_data), self.flash.data)
156159
# simulate continuous reading, informally
160+
while not (yield self.dut.rdy):
161+
yield
157162
yield self.dut.ack.eq(1)
158-
for _ in range(10*self.dut._divisor_val):
163+
for _ in range(10*self.divisor):
159164
yield
160165

161166
simulation_test(m, process)

0 commit comments

Comments
 (0)