@@ -41,13 +41,27 @@ 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 = 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 ):
45
46
if protocol not in ["standard" , "dual" , "quad" ]:
46
47
raise ValueError ("Invalid SPI protocol {!r}; must be one of "
47
48
"\" standard\" , \" dual\" , or \" quad\" "
48
49
.format (protocol ))
49
50
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 ))
50
63
self ._data_width = data_width
64
+ self ._granularity = granularity
51
65
52
66
if divisor < 1 :
53
67
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
83
97
self ._fcmd_width = self .cmd_width * self .spi_width
84
98
# A two-way register storing the current value on the DQ I/O pins
85
99
# (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
87
101
self .counter = Signal .like (self .divisor )
88
102
self .clk_posedge_next = Signal ()
89
103
self .clk_negedge_next = Signal ()
90
104
91
105
def _add_spi_hardware_logic (self , platform , module ):
92
106
"""Add the foundamental hardware logic for controlling all SPI pins to be used
93
107
"""
108
+ if self .shreg is None :
109
+ raise NotImplementedError ("Shift register shreg has not been defined!" )
94
110
shreg = self .shreg
95
111
counter = self .counter
96
112
@@ -137,30 +153,33 @@ def _add_spi_hardware_logic(self, platform, module):
137
153
# Countdown logic for counter based on divisor
138
154
# Also implements MISO logic
139
155
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 )
164
183
165
184
# MOSI logic for Standard SPI protocol:
166
185
# MOSI always output the leftmost bit of shreg
@@ -203,7 +222,7 @@ def elaborate(self, platform):
203
222
# fcmd: get formatted command based on cmd_dict
204
223
fcmd = self ._format_cmd ()
205
224
# 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 )
207
226
# FSM
208
227
with m .FSM () as fsm :
209
228
state_durations = {
@@ -311,7 +330,7 @@ def elaborate(self, platform):
311
330
# fcmd: get formatted command based on cmd_dict
312
331
fcmd = self ._format_cmd ()
313
332
# 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 )
315
334
# FSM
316
335
with m .FSM () as fsm :
317
336
state_durations = {
0 commit comments