@@ -150,14 +150,24 @@ def __init__(self, port_from, port_to, reverse=False):
150150
151151 # # #
152152
153- ratio = port_to .data_width // port_from .data_width
154- mode = port_from .mode
153+ ratio = port_to .data_width // port_from .data_width
154+ mode = port_from .mode
155+ chunk_bits = log2_int (ratio )
155156
156157 # Command ----------------------------------------------------------------------------------
157158
158- # Defines cmd type and the chunks that have been requested for the current port_to command.
159- sel = Signal (ratio )
160- cmd_buffer = stream .SyncFIFO ([("sel" , ratio ), ("we" , 1 )], 0 )
159+ # Store the subword request order for the current port_to command.
160+ # This preserves command order for reads and maps write data back to address lanes.
161+ cmd_count = Signal (max = ratio + 1 )
162+ cmd_order = Signal (ratio * chunk_bits )
163+ cmd_chunks = Signal (ratio )
164+ cmd_sel = Signal (ratio )
165+ cmd_selected = Signal ()
166+ cmd_buffer = stream .SyncFIFO ([
167+ ("we" , 1 ),
168+ ("count" , len (cmd_count )),
169+ ("order" , len (cmd_order )),
170+ ], 0 )
161171 self .submodules += cmd_buffer
162172 # Store last received command.
163173 cmd_addr = Signal .like (port_from .cmd .addr )
@@ -166,7 +176,7 @@ def __init__(self, port_from, port_to, reverse=False):
166176 # Indicates that we need to proceed to the next port_to command.
167177 next_cmd = Signal ()
168178 addr_changed = Signal ()
169- # Signals that indicate that write/read convertion has finished.
179+ # Signals that indicate that write/read conversion has finished.
170180 wdata_finished = Signal ()
171181 rdata_finished = Signal ()
172182 # Used to prevent reading old memory value if previous command has written the same address.
@@ -186,7 +196,9 @@ def __init__(self, port_from, port_to, reverse=False):
186196 NextValue (cmd_addr , port_from .cmd .addr ),
187197 NextValue (cmd_we , port_from .cmd .we ),
188198 NextValue (cmd_last , port_from .cmd .last ),
189- NextValue (sel , 1 << port_from .cmd .addr [:log2_int (ratio )]),
199+ NextValue (cmd_count , 1 ),
200+ NextValue (cmd_order [:chunk_bits ], port_from .cmd .addr [:chunk_bits ]),
201+ NextValue (cmd_chunks , cmd_sel ),
190202 If (port_from .cmd .we ,
191203 NextState ("FILL" ),
192204 ).Else (
@@ -197,7 +209,7 @@ def __init__(self, port_from, port_to, reverse=False):
197209 fsm .act ("CMD" ,
198210 port_to .cmd .valid .eq (1 ),
199211 port_to .cmd .we .eq (cmd_we ),
200- port_to .cmd .addr .eq (cmd_addr [log2_int ( ratio ) :]),
212+ port_to .cmd .addr .eq (cmd_addr [chunk_bits :]),
201213 If (port_to .cmd .ready ,
202214 If (cmd_we ,
203215 NextState ("NEW" )
@@ -206,21 +218,30 @@ def __init__(self, port_from, port_to, reverse=False):
206218 )
207219 )
208220 )
221+ cmd_order_cases = {}
222+ for i in range (ratio ):
223+ cmd_order_cases [i ] = NextValue (
224+ cmd_order [i * chunk_bits :(i + 1 )* chunk_bits ],
225+ port_from .cmd .addr [:chunk_bits ])
226+
209227 fsm .act ("FILL" ,
210228 If (next_cmd ,
211229 NextState ("COMMIT" )
212- ).Else ( # Acknowledge incomming commands, while filling `sel` .
230+ ).Else ( # Acknowledge incoming commands, while filling the request order .
213231 port_from .cmd .ready .eq (port_from .cmd .valid ),
214232 NextValue (cmd_last , port_from .cmd .last ),
215233 If (port_from .cmd .valid ,
216- NextValue (sel , sel | 1 << port_from .cmd .addr [:log2_int (ratio )])
234+ NextValue (cmd_count , cmd_count + 1 ),
235+ Case (cmd_count , cmd_order_cases ),
236+ NextValue (cmd_chunks , cmd_chunks | cmd_sel )
217237 )
218238 )
219239 )
220240 fsm .act ("COMMIT" ,
221241 cmd_buffer .sink .valid .eq (1 ),
222- cmd_buffer .sink .sel .eq (sel ),
223242 cmd_buffer .sink .we .eq (cmd_we ),
243+ cmd_buffer .sink .count .eq (cmd_count ),
244+ cmd_buffer .sink .order .eq (cmd_order ),
224245 If (cmd_buffer .sink .ready ,
225246 If (cmd_we ,
226247 NextState ("CMD" )
@@ -231,18 +252,22 @@ def __init__(self, port_from, port_to, reverse=False):
231252 )
232253
233254 self .comb += [
255+ cmd_sel .eq (1 << port_from .cmd .addr [:log2_int (ratio )]),
256+ cmd_selected .eq ((cmd_chunks & cmd_sel ) != 0 ),
234257 cmd_buffer .source .ready .eq (wdata_finished | rdata_finished ),
235- addr_changed .eq (cmd_addr [log2_int ( ratio ) :] != port_from .cmd .addr [log2_int ( ratio ) :]),
258+ addr_changed .eq (cmd_addr [chunk_bits :] != port_from .cmd .addr [chunk_bits :]),
236259 # Collision happens on write to read transition when address does not change.
237260 rw_collision .eq (cmd_we & (port_from .cmd .valid & ~ port_from .cmd .we ) & ~ addr_changed ),
238261 # Go to the next command if one of the following happens:
239262 # - port_to address changes.
240263 # - cmd type changes.
264+ # - the requested chunk has already been selected.
241265 # - we received all the `ratio` commands.
242266 # - this is the last command in a sequence.
243267 # - master requests a flush (even after the command has been sent).
244- next_cmd .eq (addr_changed | (cmd_we != port_from .cmd .we ) | (sel == 2 ** ratio - 1 )
245- | cmd_last | port_from .flush ),
268+ next_cmd .eq (addr_changed | (cmd_we != port_from .cmd .we )
269+ | (port_from .cmd .valid & cmd_selected )
270+ | (cmd_count == ratio ) | cmd_last | port_from .flush ),
246271 ]
247272
248273 self .sync += [
@@ -261,102 +286,103 @@ def __init__(self, port_from, port_to, reverse=False):
261286 # Read Datapath ----------------------------------------------------------------------------
262287
263288 if mode in ["read" , "both" ]:
264- # Queue received data not to loose it when it comes too fast.
289+ # Queue received data not to lose it when it comes too fast.
265290 rdata_fifo = stream .SyncFIFO (port_to .rdata .description , ratio - 1 )
266- rdata_converter = stream .StrideConverter (
267- description_from = port_to .rdata .description ,
268- description_to = port_from .rdata .description ,
269- reverse = reverse )
270- self .submodules += rdata_fifo , rdata_converter
271-
272- # Shift register with a bitmask of current chunk.
273- rdata_chunk = Signal (ratio , reset = 1 )
274- rdata_chunk_valid = Signal ()
275- self .sync += \
276- If (rdata_converter .source .valid &
277- rdata_converter .source .ready ,
278- rdata_chunk .eq (Cat (rdata_chunk [ratio - 1 ], rdata_chunk [:ratio - 1 ]))
279- )
291+ self .submodules += rdata_fifo
292+
293+ rdata_count = Signal (max = ratio )
294+ rdata_chunk = Signal (chunk_bits )
295+ rdata_valid = Signal ()
296+
297+ rdata_order_cases = {}
298+ rdata_mux_cases = {}
299+ for i in range (ratio ):
300+ n = ratio - 1 - i if reverse else i
301+ rdata_order_cases [i ] = rdata_chunk .eq (
302+ cmd_buffer .source .order [i * chunk_bits :(i + 1 )* chunk_bits ])
303+ rdata_mux_cases [i ] = port_from .rdata .data .eq (
304+ rdata_fifo .source .data [
305+ n * port_from .data_width :(n + 1 )* port_from .data_width ])
280306
281307 self .comb += [
282- # port_to -> rdata_fifo -> rdata_converter -> port_from
308+ # port_to -> rdata_fifo -> order mux -> port_from
283309 port_to .rdata .connect (rdata_fifo .sink ),
284- rdata_fifo . source . connect ( rdata_converter . sink ),
285- rdata_chunk_valid . eq (( cmd_buffer . source . sel & rdata_chunk ) != 0 ),
286- If (cmd_buffer .source .valid & ~ cmd_buffer .source .we ,
287- # If that chunk is valid we send it to the user port and wait for ready.
288- If ( rdata_chunk_valid ,
289- port_from .rdata .valid . eq ( rdata_converter . source . valid ),
290- port_from . rdata . data . eq ( rdata_converter .source .data ),
291- rdata_converter . source . ready . eq ( port_from . rdata . ready )
292- ). Else ( # If this chunk was not requested in `sel`, ignore it.
293- rdata_converter . source . ready . eq ( 1 )
294- ),
295- rdata_finished . eq ( rdata_converter . source . valid & rdata_converter . source .ready
296- & rdata_chunk [ ratio - 1 ] )
297- ),
310+ Case ( rdata_count , rdata_order_cases ),
311+ Case ( rdata_chunk , rdata_mux_cases ),
312+ rdata_valid . eq (cmd_buffer .source .valid & ~ cmd_buffer .source .we & rdata_fifo . source . valid ) ,
313+ port_from . rdata . valid . eq ( rdata_valid ),
314+ rdata_fifo . source . ready . eq ( rdata_finished ) ,
315+ rdata_finished . eq ( rdata_valid & port_from .rdata .ready
316+ & ( rdata_count == ( cmd_buffer .source .count - 1 )) ),
317+ ]
318+ self . sync += [
319+ If ( rdata_finished ,
320+ rdata_count . eq ( 0 )
321+ ). Elif ( rdata_valid & port_from . rdata .ready ,
322+ rdata_count . eq ( rdata_count + 1 )
323+ )
298324 ]
299325
300326 # Write Datapath ---------------------------------------------------------------------------
301327
302328 if mode in ["write" , "both" ]:
303- # Queue write data not to miss it when the lower chunks haven't been reqested .
329+ # Queue write data not to miss it when the lower chunks haven't been requested .
304330 wdata_fifo = stream .SyncFIFO (port_from .wdata .description , ratio - 1 )
305331 wdata_buffer = stream .SyncFIFO (port_to .wdata .description , 1 )
306- wdata_converter = stream .StrideConverter (
307- description_from = port_from .wdata .description ,
308- description_to = port_to .wdata .description ,
309- reverse = reverse )
310- self .submodules += wdata_converter , wdata_fifo , wdata_buffer
311-
312- # Shift register with a bitmask of current chunk.
313- wdata_chunk = Signal (ratio , reset = 1 )
314- wdata_chunk_valid = Signal ()
315- self .sync += \
316- If (wdata_converter .sink .valid & wdata_converter .sink .ready ,
317- wdata_chunk .eq (Cat (wdata_chunk [ratio - 1 ], wdata_chunk [:ratio - 1 ]))
318- )
319-
320- # Replicate `sel` bits to match the width of port_to.wdata.we.
321- wdata_sel = Signal .like (port_to .wdata .we )
322- if reverse :
323- wdata_sel_parts = [
324- Replicate (cmd_buffer .source .sel [i ], port_to .wdata .we .nbits // sel .nbits )
325- for i in reversed (range (ratio ))
326- ]
327- else :
328- wdata_sel_parts = [
329- Replicate (cmd_buffer .source .sel [i ], port_to .wdata .we .nbits // sel .nbits )
330- for i in range (ratio )
332+ self .submodules += wdata_fifo , wdata_buffer
333+
334+ wdata_count = Signal (max = ratio )
335+ wdata_chunk = Signal (chunk_bits )
336+ wdata_data = Signal .like (port_to .wdata .data )
337+ wdata_we = Signal .like (port_to .wdata .we )
338+ wdata_pending = Signal ()
339+ wdata_accept = Signal ()
340+ wdata_last = Signal ()
341+
342+ wdata_order_cases = {}
343+ wdata_store_cases = {}
344+ for i in range (ratio ):
345+ n = ratio - 1 - i if reverse else i
346+ wdata_order_cases [i ] = wdata_chunk .eq (
347+ cmd_buffer .source .order [i * chunk_bits :(i + 1 )* chunk_bits ])
348+ wdata_store_cases [i ] = [
349+ wdata_data [
350+ n * port_from .data_width :(n + 1 )* port_from .data_width
351+ ].eq (wdata_fifo .source .data ),
352+ wdata_we [
353+ n * port_from .wdata .we .nbits :(n + 1 )* port_from .wdata .we .nbits
354+ ].eq (wdata_fifo .source .we ),
331355 ]
332356
333- self .sync += \
334- If (cmd_buffer .source .valid & cmd_buffer .source .we & wdata_chunk [ratio - 1 ],
335- wdata_sel .eq (Cat (wdata_sel_parts ))
336- )
337-
338357 self .comb += [
339- # port_from -> wdata_fifo -> wdata_converter
358+ # port_from -> wdata_fifo -> ordered wide buffer -> port_to
340359 port_from .wdata .connect (wdata_fifo .sink ),
341360 wdata_buffer .source .connect (port_to .wdata ),
342- wdata_chunk_valid .eq ((cmd_buffer .source .sel & wdata_chunk ) != 0 ),
343- If (cmd_buffer .source .valid & cmd_buffer .source .we ,
344- # When the current chunk is valid, read it from wdata_fifo.
345- If (wdata_chunk_valid ,
346- wdata_converter .sink .valid .eq (wdata_fifo .source .valid ),
347- wdata_converter .sink .data .eq (wdata_fifo .source .data ),
348- wdata_converter .sink .we .eq (wdata_fifo .source .we ),
349- wdata_fifo .source .ready .eq (wdata_converter .sink .ready ),
350- ).Else ( # If chunk is not valid, send any data and do not advance fifo.
351- wdata_converter .sink .valid .eq (1 ),
352- ),
353- ),
354- wdata_buffer .sink .valid .eq (wdata_converter .source .valid ),
355- wdata_buffer .sink .data .eq (wdata_converter .source .data ),
356- wdata_buffer .sink .we .eq (wdata_converter .source .we & wdata_sel ),
357- wdata_converter .source .ready .eq (wdata_buffer .sink .ready ),
358- wdata_finished .eq (wdata_converter .sink .valid & wdata_converter .sink .ready
359- & wdata_chunk [ratio - 1 ]),
361+ wdata_buffer .sink .valid .eq (wdata_pending ),
362+ wdata_buffer .sink .data .eq (wdata_data ),
363+ wdata_buffer .sink .we .eq (wdata_we ),
364+ Case (wdata_count , wdata_order_cases ),
365+ wdata_fifo .source .ready .eq (cmd_buffer .source .valid & cmd_buffer .source .we
366+ & ~ wdata_pending ),
367+ wdata_accept .eq (wdata_fifo .source .valid & wdata_fifo .source .ready ),
368+ wdata_last .eq (wdata_count == (cmd_buffer .source .count - 1 )),
369+ wdata_finished .eq (wdata_buffer .sink .valid & wdata_buffer .sink .ready ),
370+ ]
371+
372+ self .sync += [
373+ If (wdata_finished ,
374+ wdata_count .eq (0 ),
375+ wdata_data .eq (0 ),
376+ wdata_we .eq (0 ),
377+ wdata_pending .eq (0 ),
378+ ).Elif (wdata_accept ,
379+ Case (wdata_chunk , wdata_store_cases ),
380+ If (wdata_last ,
381+ wdata_pending .eq (1 )
382+ ).Else (
383+ wdata_count .eq (wdata_count + 1 )
384+ )
385+ )
360386 ]
361387
362388# LiteDRAMNativePortConverter ----------------------------------------------------------------------
0 commit comments