Skip to content

Commit ef12c2a

Browse files
committed
spi: use granularity (defaults to 8); improve counter logic
1 parent e941997 commit ef12c2a

File tree

2 files changed

+50
-28
lines changed

2 files changed

+50
-28
lines changed

nmigen_stdio/spiflash.py

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

44-
def __init__(self, *, protocol, data_width=32, divisor=1, divisor_bits=None, pins=None):
44+
def __init__(self, *, protocol, data_width=32, granularity=8,
45+
divisor=1, divisor_bits=None, pins=None):
4546
if protocol not in ["standard", "dual", "quad"]:
4647
raise ValueError("Invalid SPI protocol {!r}; must be one of "
4748
"\"standard\", \"dual\", or \"quad\""
4849
.format(protocol))
4950
self._protocol = protocol
51+
if not isinstance(data_width, int) or data_width < 1:
52+
raise ValueError("Data width must be a positive integer, not {}"
53+
.format(repr(data_width)))
54+
if not isinstance(granularity, int) or granularity < 8 or (
55+
granularity != 1 << (granularity.bit_length()-1)
56+
):
57+
raise ValueError("Granularity must be a positive integer, "
58+
"at least 8 and a power of 2, not {}"
59+
.format(repr(granularity)))
60+
if data_width < granularity or data_width % granularity:
61+
raise ValueError("Data width must be divisible by granularity ({} % {} != 0)"
62+
.format(data_width, granularity))
5063
self._data_width = data_width
64+
self._granularity = granularity
5165

5266
if divisor < 1:
5367
raise ValueError("Invalid divisor value; must be at least 1"
@@ -83,14 +97,16 @@ def __init__(self, *, protocol, data_width=32, divisor=1, divisor_bits=None, pin
8397
self._fcmd_width = self.cmd_width * self.spi_width
8498
# A two-way register storing the current value on the DQ I/O pins
8599
# (Note: DQ pins are both input/output only for Dual or Quad SPI protocols)
86-
self.shreg = Signal(max(self._fcmd_width, self._data_width))
100+
self.shreg = None
87101
self.counter = Signal.like(self.divisor)
88102
self.clk_posedge_next = Signal()
89103
self.clk_negedge_next = Signal()
90104

91105
def _add_spi_hardware_logic(self, platform, module):
92106
"""Add the foundamental hardware logic for controlling all SPI pins to be used
93107
"""
108+
if self.shreg is None:
109+
raise NotImplementedError("Shift register shreg has not been defined!")
94110
shreg = self.shreg
95111
counter = self.counter
96112

@@ -137,30 +153,33 @@ def _add_spi_hardware_logic(self, platform, module):
137153
# Countdown logic for counter based on divisor
138154
# Also implements MISO logic
139155
dq_i = Signal(self.spi_width)
140-
# When countdown is half-way done, clock edge goes up (positive);
141-
# MISO starts latching bit/byte from slave
142-
with module.If((counter == (self.divisor+1) >> 1) & self.cs):
143-
module.d.sync += self.clk.eq(1)
144-
# Indicate imminent posedge
145-
module.d.comb += self.clk_posedge_next.eq(1)
146-
if self._protocol == "standard":
147-
module.d.sync += dq_i.eq(self.miso)
148-
elif self._protocol in ["dual", "quad"]:
149-
module.d.sync += dq_i.eq(self.dq.i)
150-
# When countdown reaches 0, clock edge goes down (negative)
151-
# shreg latches from MISO for r_data to read
152-
with module.If((counter == 0) & self.cs):
153-
module.d.sync += [
154-
self.clk.eq(0),
155-
counter.eq(self.divisor)
156-
]
157-
# Indicate imminent negedge
158-
module.d.comb += self.clk_negedge_next.eq(1)
159-
# MOSI latches from shreg by "pushing" old data out from the left of shreg
160-
module.d.sync += shreg.eq(Cat(dq_i, shreg[:-self.spi_width]))
161-
# Normal countdown
162-
with module.Elif(self.cs):
163-
module.d.sync += counter.eq(counter - 1)
156+
# Enable SCLK only when CS:
157+
with module.If(self.cs):
158+
# When countdown is half-way done, clock edge goes up (positive):
159+
# - MISO latches bit/byte from slave
160+
# - slave has been latching from MOSI mid-way (since previous negedge)
161+
with module.If(counter == (self.divisor+1) >> 1):
162+
module.d.sync += self.clk.eq(1)
163+
# Indicate imminent posedge
164+
module.d.comb += self.clk_posedge_next.eq(1)
165+
if self._protocol == "standard":
166+
module.d.sync += dq_i.eq(self.miso)
167+
elif self._protocol in ["dual", "quad"]:
168+
module.d.sync += dq_i.eq(self.dq.i)
169+
# When countdown reaches 0, clock edge goes down (negative):
170+
# - MOSI latches from shreg by "pushing" old data out from the left of shreg;
171+
# - slave has been outputting MISO mid-way (since previous posedge)
172+
with module.If(counter == 0):
173+
module.d.sync += [
174+
self.clk.eq(0),
175+
counter.eq(self.divisor)
176+
]
177+
# Indicate imminent negedge
178+
module.d.comb += self.clk_negedge_next.eq(1)
179+
module.d.sync += shreg.eq(Cat(dq_i, shreg[:-self.spi_width]))
180+
# Normal countdown
181+
with module.Else():
182+
module.d.sync += counter.eq(counter - 1)
164183

165184
# MOSI logic for Standard SPI protocol:
166185
# MOSI always output the leftmost bit of shreg
@@ -203,7 +222,7 @@ def elaborate(self, platform):
203222
# fcmd: get formatted command based on cmd_dict
204223
fcmd = self._format_cmd()
205224
# addr: convert bus address to byte-sized address
206-
byte_addr = Cat(Repl(0, log2_int(self._data_width//8)), self.addr)
225+
byte_addr = Cat(Repl(0, log2_int(self._data_width // self._granularity)), self.addr)
207226
# FSM
208227
with m.FSM() as fsm:
209228
state_durations = {
@@ -311,7 +330,7 @@ def elaborate(self, platform):
311330
# fcmd: get formatted command based on cmd_dict
312331
fcmd = self._format_cmd()
313332
# addr: convert bus address to byte-sized address
314-
byte_addr = Cat(Repl(0, log2_int(self._data_width//8)), self.addr)
333+
byte_addr = Cat(Repl(0, log2_int(self._data_width // self._granularity)), self.addr)
315334
# FSM
316335
with m.FSM() as fsm:
317336
state_durations = {

nmigen_stdio/test/test_spiflash.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ def test_standard(self):
106106
self.dut = SPIFlashFastReader(protocol="standard",
107107
addr_width=24,
108108
data_width=32,
109+
granularity=8,
109110
divisor=self.divisor,
110111
dummy_cycles=15)
111112
self.flash = (SPIFlashFastReadTestCase.
@@ -120,6 +121,7 @@ def test_dual(self):
120121
self.dut = SPIFlashFastReader(protocol="dual",
121122
addr_width=24,
122123
data_width=32,
124+
granularity=8,
123125
divisor=self.divisor,
124126
dummy_cycles=15)
125127
self.flash = (SPIFlashFastReadTestCase.
@@ -134,6 +136,7 @@ def test_quad(self):
134136
self.dut = SPIFlashFastReader(protocol="quad",
135137
addr_width=24,
136138
data_width=32,
139+
granularity=8,
137140
divisor=self.divisor,
138141
dummy_cycles=15)
139142
self.flash = (SPIFlashFastReadTestCase.

0 commit comments

Comments
 (0)