frontend/adaptation: use port.cmd.last instead of port.flush in up-converter

This commit is contained in:
Jędrzej Boczar 2020-05-11 15:28:32 +02:00
parent 35fa91c055
commit 1587ee3611
3 changed files with 33 additions and 44 deletions

View File

@ -158,28 +158,27 @@ class LiteDRAMNativePortUpConverter(Module):
sel = Signal(ratio)
cmd_buffer = stream.SyncFIFO([("sel", ratio), ("we", 1)], 4)
self.submodules += cmd_buffer
# store last command info
# store last received command
cmd_addr = Signal.like(port_from.cmd.addr)
cmd_we = Signal()
cmd_last = Signal()
# indicates that we need to proceed to the next port_to command
next_cmd = Signal()
# signals that indicate that write/read convertion has finished
wdata_finished = Signal()
rdata_finished = Signal()
# register that user requested a flush
flush_r = Signal()
self.comb += [
# go to the next command if one of the following happens:
# * port_to address changes
# * cmd type changes
# * we received all the `ratio` commands
# * this is last command (flush)
# * this is the last command in a sequence
next_cmd.eq(
(cmd_addr[log2_int(ratio):] != port_from.cmd.addr[log2_int(ratio):]) |
(cmd_we != port_from.cmd.we) |
(sel == 2**ratio - 1) |
port_from.flush | flush_r
(cmd_addr[log2_int(ratio):] != port_from.cmd.addr[log2_int(ratio):])
| (cmd_we != port_from.cmd.we)
| (sel == 2**ratio - 1)
| cmd_last
),
# when the first command is received, send it immediatelly
If(sel == 0,
@ -208,16 +207,12 @@ class LiteDRAMNativePortUpConverter(Module):
If(port_from.cmd.valid & port_from.cmd.ready,
cmd_addr.eq(port_from.cmd.addr),
cmd_we.eq(port_from.cmd.we),
cmd_last.eq(port_from.cmd.last),
sel.eq(sel | (1 << port_from.cmd.addr[:log2_int(ratio)])),
),
# clear `sel` after the command has been sent for data procesing
If(cmd_buffer.sink.valid & cmd_buffer.sink.ready,
sel.eq(0),
flush_r.eq(0),
),
# store flush info until we register the current command
If(port_from.flush,
flush_r.eq(1)
),
]

View File

@ -94,8 +94,10 @@ class NativePortDriver:
for _ in range(latency):
yield
def read(self, address, wait_data=True):
def read(self, address, first=0, last=0, wait_data=True):
yield self.port.cmd.valid.eq(1)
yield self.port.cmd.first.eq(first)
yield self.port.cmd.last.eq(last)
yield self.port.cmd.we.eq(0)
yield self.port.cmd.addr.eq(address)
yield
@ -108,10 +110,12 @@ class NativePortDriver:
yield
return self.rdata[-1]
def write(self, address, data, we=None, wait_data=True, data_with_cmd=False):
def write(self, address, data, we=None, first=0, last=0, wait_data=True, data_with_cmd=False):
if we is None:
we = 2**self.port.wdata.we.nbits - 1
yield self.port.cmd.valid.eq(1)
yield self.port.cmd.first.eq(first)
yield self.port.cmd.last.eq(last)
yield self.port.cmd.we.eq(1)
yield self.port.cmd.addr.eq(address)
if data_with_cmd:

View File

@ -50,15 +50,13 @@ class ConverterDUT(Module):
self.submodules.converter = LiteDRAMNativePortConverter(
self.write_user_port, self.write_crossbar_port)
def read(self, address, wait_data=True):
return (yield from self.read_driver.read(address, wait_data=wait_data))
def read(self, address, **kwargs):
return (yield from self.read_driver.read(address, **kwargs))
def write(self, address, data, wait_data=True, we=None):
data_with_cmd = False
def write(self, address, data, **kwargs):
if self.write_user_port.data_width > self.write_crossbar_port.data_width:
data_with_cmd = True
return (yield from self.write_driver.write(address, data, we, wait_data=wait_data,
data_with_cmd=data_with_cmd))
kwargs["data_with_cmd"] = True
return (yield from self.write_driver.write(address, data, **kwargs))
class CDCDUT(ConverterDUT):
@ -99,14 +97,13 @@ class TestAdaptation(MemoryTestDataMixin, unittest.TestCase):
for adr, data in pattern:
yield from dut.write(adr, data)
for adr, _ in pattern:
for adr, _ in pattern[:-1]:
yield from dut.read(adr, wait_data=False)
# we need to flush after last command in the up-converter case, if the last
# command does not fill whole `sel`
yield dut.write_user_port.flush.eq(1)
yield
yield dut.write_user_port.flush.eq(0)
# use cmd.last to indicate last command in the sequence
# this is needed for the cases in up-converter when it cannot be deduced
# that port_to.cmd should be sent
adr, _ = pattern[-1]
yield from dut.read(adr, wait_data=False, last=1)
yield from dut.write_driver.wait_all()
yield from dut.read_driver.wait_all()
@ -123,8 +120,7 @@ class TestAdaptation(MemoryTestDataMixin, unittest.TestCase):
self.assertEqual(dut.read_driver.rdata, [data for adr, data in pattern])
def converter_test(self, test_data, user_data_width, native_data_width, **kwargs):
# for separate_rw in [True, False]:
for separate_rw in [False]:
for separate_rw in [True, False]:
with self.subTest(separate_rw=separate_rw):
data = self.pattern_test_data[test_data]
dut = ConverterDUT(user_data_width=user_data_width, native_data_width=native_data_width,
@ -209,7 +205,7 @@ class TestAdaptation(MemoryTestDataMixin, unittest.TestCase):
0x00000000, # 0x0c
]
for separate_rw in [False, True]:
for separate_rw in [True, False]:
with self.subTest(separate_rw=separate_rw):
dut = ConverterDUT(user_data_width=8, native_data_width=32,
mem_depth=len(mem_expected), separate_rw=separate_rw)
@ -217,14 +213,11 @@ class TestAdaptation(MemoryTestDataMixin, unittest.TestCase):
main_generator=main_generator)
def test_up_converter_write_with_manual_flush(self):
# Verify that up-conversion writes incomplete data when flushed
# Verify that up-conversion writes incomplete data when it receives cmd.last
def main_generator(dut):
yield from dut.write(0x00, 0x11, wait_data=False)
yield from dut.write(0x01, 0x22, wait_data=False)
yield from dut.write(0x02, 0x33, wait_data=False)
yield dut.write_user_port.flush.eq(1)
yield
yield dut.write_user_port.flush.eq(0)
yield from dut.write(0x02, 0x33, wait_data=False, last=1)
yield from dut.write_driver.wait_all()
for _ in range(8): # wait for memory
@ -238,7 +231,7 @@ class TestAdaptation(MemoryTestDataMixin, unittest.TestCase):
0x00000000, # 0x0c
]
for separate_rw in [False, True]:
for separate_rw in [True, False]:
with self.subTest(separate_rw=separate_rw):
dut = ConverterDUT(user_data_width=8, native_data_width=32,
mem_depth=len(mem_expected), separate_rw=separate_rw)
@ -269,7 +262,7 @@ class TestAdaptation(MemoryTestDataMixin, unittest.TestCase):
]
for separate_rw in [False, True]:
for separate_rw in [True, False]:
with self.subTest(separate_rw=separate_rw):
dut = ConverterDUT(user_data_width=8, native_data_width=32,
mem_depth=len(mem_expected), separate_rw=separate_rw)
@ -312,7 +305,7 @@ class TestAdaptation(MemoryTestDataMixin, unittest.TestCase):
(0x01, mem_expected[1]),
]
for separate_rw in [False, True]:
for separate_rw in [True, False]:
with self.subTest(separate_rw=separate_rw):
dut = ConverterDUT(user_data_width=8, native_data_width=32,
mem_depth=len(mem_expected), separate_rw=separate_rw)
@ -324,10 +317,7 @@ class TestAdaptation(MemoryTestDataMixin, unittest.TestCase):
def main_generator(dut):
yield from dut.write(0x00, 0x11, wait_data=False)
yield from dut.write(0x02, 0x22, wait_data=False)
yield from dut.write(0x03, 0x33, wait_data=False)
yield dut.write_user_port.flush.eq(1)
yield
yield dut.write_user_port.flush.eq(0)
yield from dut.write(0x03, 0x33, wait_data=False, last=1)
yield from dut.write_driver.wait_all()
for _ in range(8): # wait for memory