@@ -150,16 +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 )
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 )
160164 cmd_sel = Signal (ratio )
161165 cmd_selected = Signal ()
162- cmd_buffer = stream .SyncFIFO ([("sel" , ratio ), ("we" , 1 )], 0 )
166+ cmd_buffer = stream .SyncFIFO ([
167+ ("we" , 1 ),
168+ ("count" , len (cmd_count )),
169+ ("order" , len (cmd_order )),
170+ ], 0 )
163171 self .submodules += cmd_buffer
164172 # Store last received command.
165173 cmd_addr = Signal .like (port_from .cmd .addr )
@@ -168,7 +176,7 @@ def __init__(self, port_from, port_to, reverse=False):
168176 # Indicates that we need to proceed to the next port_to command.
169177 next_cmd = Signal ()
170178 addr_changed = Signal ()
171- # Signals that indicate that write/read convertion has finished.
179+ # Signals that indicate that write/read conversion has finished.
172180 wdata_finished = Signal ()
173181 rdata_finished = Signal ()
174182 # Used to prevent reading old memory value if previous command has written the same address.
@@ -188,7 +196,9 @@ def __init__(self, port_from, port_to, reverse=False):
188196 NextValue (cmd_addr , port_from .cmd .addr ),
189197 NextValue (cmd_we , port_from .cmd .we ),
190198 NextValue (cmd_last , port_from .cmd .last ),
191- NextValue (sel , cmd_sel ),
199+ NextValue (cmd_count , 1 ),
200+ NextValue (cmd_order [:chunk_bits ], port_from .cmd .addr [:chunk_bits ]),
201+ NextValue (cmd_chunks , cmd_sel ),
192202 If (port_from .cmd .we ,
193203 NextState ("FILL" ),
194204 ).Else (
@@ -199,7 +209,7 @@ def __init__(self, port_from, port_to, reverse=False):
199209 fsm .act ("CMD" ,
200210 port_to .cmd .valid .eq (1 ),
201211 port_to .cmd .we .eq (cmd_we ),
202- port_to .cmd .addr .eq (cmd_addr [log2_int ( ratio ) :]),
212+ port_to .cmd .addr .eq (cmd_addr [chunk_bits :]),
203213 If (port_to .cmd .ready ,
204214 If (cmd_we ,
205215 NextState ("NEW" )
@@ -208,21 +218,30 @@ def __init__(self, port_from, port_to, reverse=False):
208218 )
209219 )
210220 )
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+
211227 fsm .act ("FILL" ,
212228 If (next_cmd ,
213229 NextState ("COMMIT" )
214- ).Else ( # Acknowledge incomming commands, while filling `sel` .
230+ ).Else ( # Acknowledge incoming commands, while filling the request order .
215231 port_from .cmd .ready .eq (port_from .cmd .valid ),
216232 NextValue (cmd_last , port_from .cmd .last ),
217233 If (port_from .cmd .valid ,
218- NextValue (sel , sel | cmd_sel )
234+ NextValue (cmd_count , cmd_count + 1 ),
235+ Case (cmd_count , cmd_order_cases ),
236+ NextValue (cmd_chunks , cmd_chunks | cmd_sel )
219237 )
220238 )
221239 )
222240 fsm .act ("COMMIT" ,
223241 cmd_buffer .sink .valid .eq (1 ),
224- cmd_buffer .sink .sel .eq (sel ),
225242 cmd_buffer .sink .we .eq (cmd_we ),
243+ cmd_buffer .sink .count .eq (cmd_count ),
244+ cmd_buffer .sink .order .eq (cmd_order ),
226245 If (cmd_buffer .sink .ready ,
227246 If (cmd_we ,
228247 NextState ("CMD" )
@@ -234,9 +253,9 @@ def __init__(self, port_from, port_to, reverse=False):
234253
235254 self .comb += [
236255 cmd_sel .eq (1 << port_from .cmd .addr [:log2_int (ratio )]),
237- cmd_selected .eq ((sel & cmd_sel ) != 0 ),
256+ cmd_selected .eq ((cmd_chunks & cmd_sel ) != 0 ),
238257 cmd_buffer .source .ready .eq (wdata_finished | rdata_finished ),
239- 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 :]),
240259 # Collision happens on write to read transition when address does not change.
241260 rw_collision .eq (cmd_we & (port_from .cmd .valid & ~ port_from .cmd .we ) & ~ addr_changed ),
242261 # Go to the next command if one of the following happens:
@@ -248,7 +267,7 @@ def __init__(self, port_from, port_to, reverse=False):
248267 # - master requests a flush (even after the command has been sent).
249268 next_cmd .eq (addr_changed | (cmd_we != port_from .cmd .we )
250269 | (port_from .cmd .valid & cmd_selected )
251- | (sel == 2 ** ratio - 1 ) | cmd_last | port_from .flush ),
270+ | (cmd_count == ratio ) | cmd_last | port_from .flush ),
252271 ]
253272
254273 self .sync += [
@@ -267,102 +286,103 @@ def __init__(self, port_from, port_to, reverse=False):
267286 # Read Datapath ----------------------------------------------------------------------------
268287
269288 if mode in ["read" , "both" ]:
270- # 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.
271290 rdata_fifo = stream .SyncFIFO (port_to .rdata .description , ratio - 1 )
272- rdata_converter = stream .StrideConverter (
273- description_from = port_to .rdata .description ,
274- description_to = port_from .rdata .description ,
275- reverse = reverse )
276- self .submodules += rdata_fifo , rdata_converter
277-
278- # Shift register with a bitmask of current chunk.
279- rdata_chunk = Signal (ratio , reset = 1 )
280- rdata_chunk_valid = Signal ()
281- self .sync += \
282- If (rdata_converter .source .valid &
283- rdata_converter .source .ready ,
284- rdata_chunk .eq (Cat (rdata_chunk [ratio - 1 ], rdata_chunk [:ratio - 1 ]))
285- )
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 ])
286306
287307 self .comb += [
288- # port_to -> rdata_fifo -> rdata_converter -> port_from
308+ # port_to -> rdata_fifo -> order mux -> port_from
289309 port_to .rdata .connect (rdata_fifo .sink ),
290- rdata_fifo . source . connect ( rdata_converter . sink ),
291- rdata_chunk_valid . eq (( cmd_buffer . source . sel & rdata_chunk ) != 0 ),
292- If (cmd_buffer .source .valid & ~ cmd_buffer .source .we ,
293- # If that chunk is valid we send it to the user port and wait for ready.
294- If ( rdata_chunk_valid ,
295- port_from .rdata .valid . eq ( rdata_converter . source . valid ),
296- port_from . rdata . data . eq ( rdata_converter .source .data ),
297- rdata_converter . source . ready . eq ( port_from . rdata . ready )
298- ). Else ( # If this chunk was not requested in `sel`, ignore it.
299- rdata_converter . source . ready . eq ( 1 )
300- ),
301- rdata_finished . eq ( rdata_converter . source . valid & rdata_converter . source .ready
302- & rdata_chunk [ ratio - 1 ] )
303- ),
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+ )
304324 ]
305325
306326 # Write Datapath ---------------------------------------------------------------------------
307327
308328 if mode in ["write" , "both" ]:
309- # 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 .
310330 wdata_fifo = stream .SyncFIFO (port_from .wdata .description , ratio - 1 )
311331 wdata_buffer = stream .SyncFIFO (port_to .wdata .description , 1 )
312- wdata_converter = stream .StrideConverter (
313- description_from = port_from .wdata .description ,
314- description_to = port_to .wdata .description ,
315- reverse = reverse )
316- self .submodules += wdata_converter , wdata_fifo , wdata_buffer
317-
318- # Shift register with a bitmask of current chunk.
319- wdata_chunk = Signal (ratio , reset = 1 )
320- wdata_chunk_valid = Signal ()
321- self .sync += \
322- If (wdata_converter .sink .valid & wdata_converter .sink .ready ,
323- wdata_chunk .eq (Cat (wdata_chunk [ratio - 1 ], wdata_chunk [:ratio - 1 ]))
324- )
325-
326- # Replicate `sel` bits to match the width of port_to.wdata.we.
327- wdata_sel = Signal .like (port_to .wdata .we )
328- if reverse :
329- wdata_sel_parts = [
330- Replicate (cmd_buffer .source .sel [i ], port_to .wdata .we .nbits // sel .nbits )
331- for i in reversed (range (ratio ))
332- ]
333- else :
334- wdata_sel_parts = [
335- Replicate (cmd_buffer .source .sel [i ], port_to .wdata .we .nbits // sel .nbits )
336- 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 ),
337355 ]
338356
339- self .sync += \
340- If (cmd_buffer .source .valid & cmd_buffer .source .we & wdata_chunk [ratio - 1 ],
341- wdata_sel .eq (Cat (wdata_sel_parts ))
342- )
343-
344357 self .comb += [
345- # port_from -> wdata_fifo -> wdata_converter
358+ # port_from -> wdata_fifo -> ordered wide buffer -> port_to
346359 port_from .wdata .connect (wdata_fifo .sink ),
347360 wdata_buffer .source .connect (port_to .wdata ),
348- wdata_chunk_valid .eq ((cmd_buffer .source .sel & wdata_chunk ) != 0 ),
349- If (cmd_buffer .source .valid & cmd_buffer .source .we ,
350- # When the current chunk is valid, read it from wdata_fifo.
351- If (wdata_chunk_valid ,
352- wdata_converter .sink .valid .eq (wdata_fifo .source .valid ),
353- wdata_converter .sink .data .eq (wdata_fifo .source .data ),
354- wdata_converter .sink .we .eq (wdata_fifo .source .we ),
355- wdata_fifo .source .ready .eq (wdata_converter .sink .ready ),
356- ).Else ( # If chunk is not valid, send any data and do not advance fifo.
357- wdata_converter .sink .valid .eq (1 ),
358- ),
359- ),
360- wdata_buffer .sink .valid .eq (wdata_converter .source .valid ),
361- wdata_buffer .sink .data .eq (wdata_converter .source .data ),
362- wdata_buffer .sink .we .eq (wdata_converter .source .we & wdata_sel ),
363- wdata_converter .source .ready .eq (wdata_buffer .sink .ready ),
364- wdata_finished .eq (wdata_converter .sink .valid & wdata_converter .sink .ready
365- & 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+ )
366386 ]
367387
368388# LiteDRAMNativePortConverter ----------------------------------------------------------------------
0 commit comments