Skip to content

Commit f44b9f6

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

File tree

2 files changed

+85
-74
lines changed

2 files changed

+85
-74
lines changed

nmigen_stdio/spiflash.py

Lines changed: 74 additions & 68 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,70 @@ 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+
self.cs.eq(1),
224+
counter.eq(0),
220225
self.clk.eq(0)
221226
]
222227
m.next = "SLOWREAD-CS"
223228
# State: Chip select
224229
with m.State("SLOWREAD-CS"):
225-
with m.If(counter == self._divisor_val >> 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.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.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.negedge_next):
255263
m.d.sync += [
256264
state_counter.eq(0),
257-
self.r_rdy.eq(1)
265+
self.cs.eq(0)
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)
268274
# 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):
273283
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)
280287

281288
return m
282289

@@ -313,11 +320,10 @@ def elaborate(self, platform):
313320
# FSM
314321
with m.FSM() as fsm:
315322
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
321327
}
322328
max_duration = max([dur for state, dur in state_durations.items()])
323329
# A "count-up" counter for each state of the command
@@ -327,66 +333,66 @@ def elaborate(self, platform):
327333
m.d.comb += self.rdy.eq(1)
328334
with m.If(self.ack):
329335
m.d.sync += [
330-
counter.eq(self._divisor_val),
336+
self.cs.eq(1),
337+
counter.eq(0),
331338
self.clk.eq(0)
332339
]
333340
m.next = "FASTREAD-CS"
334341
# State: Chip select
335342
with m.State("FASTREAD-CS"):
336-
with m.If(counter == self._divisor_val >> 1):
343+
with m.If(self.clk_negedge_next):
337344
m.d.sync += [
338345
state_counter.eq(0),
339346
shreg[-self.cmd_width:].eq(fcmd)
340347
]
341348
if self._protocol in ["dual", "quad"]:
342-
m.d.sync += dq_oe.eq(1)
349+
m.d.sync += self.dq_oe.eq(1)
343350
m.next = "FASTREAD-CMD"
344351
# State: Command, MOSI
345352
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):
347355
m.d.sync += [
348356
state_counter.eq(0),
349357
shreg[-self._addr_width:].eq(byte_addr)
350358
]
351359
m.next = "FASTREAD-ADDR"
352-
with m.Else():
360+
with m.Elif(self.clk_negedge_next):
353361
m.d.sync += state_counter.eq(state_counter + 1)
354362
# State: Address, MOSI
355363
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):
357366
m.d.sync += state_counter.eq(0)
358367
if self._protocol in ["dual", "quad"]:
359-
m.d.sync += dq_oe.eq(0)
368+
m.d.sync += self.dq_oe.eq(0)
360369
m.next = "FASTREAD-WAITREAD"
361-
with m.Else():
370+
with m.Elif(self.clk_negedge_next):
362371
m.d.sync += state_counter.eq(state_counter + 1)
363372
# State: Dummy cycles (waiting), and then Read (MISO)
364373
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)
370377
if self._protocol in ["dual", "quad"]:
371-
m.d.sync += dq_oe.eq(0)
378+
m.d.sync += self.dq_oe.eq(0)
372379
m.next = "FASTREAD-RDYWAIT"
373-
with m.Else():
380+
with m.Elif(self.clk_negedge_next):
374381
m.d.sync += state_counter.eq(state_counter + 1)
375382
# State: Send r_rdy (for 1 clock period), and then Wait
376383
with m.State("FASTREAD-RDYWAIT"):
377-
with m.If(state_counter == 0):
378-
m.d.sync += self.r_rdy.eq(0)
379384
# 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+
]
382391
m.next = "FASTREAD-IDLE"
383-
with m.Else():
392+
with m.Elif(self.clk_negedge_next):
384393
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)
391397

392398
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)