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

View File

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