Skip to content

Commit be6e2de

Browse files
committed
Merge master into fix-gen-axi-user-enable
Keep both AXI user-port gating and simulation CSR-map coverage.
2 parents 08170e5 + 54b33f8 commit be6e2de

10 files changed

Lines changed: 435 additions & 138 deletions

File tree

litedram/frontend/adapter.py

Lines changed: 120 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -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 ----------------------------------------------------------------------

litedram/frontend/dma.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,6 @@ def add_csr(self, default_base=0, default_length=0, default_enable=0, default_lo
249249
self.submodules.fsm = fsm
250250
self.comb += fsm.reset.eq(~self._enable.storage)
251251
fsm.act("IDLE",
252-
self.sink.ready.eq(1),
253252
NextValue(offset, 0),
254253
NextState("RUN"),
255254
)

0 commit comments

Comments
 (0)