frontend/adaptation: use port.cmd.last instead of port.flush in up-converter
This commit is contained in:
parent
35fa91c055
commit
1587ee3611
|
@ -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)
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue