diff --git a/litedram/frontend/adaptation.py b/litedram/frontend/adaptation.py index 1ce77c3..fe06963 100644 --- a/litedram/frontend/adaptation.py +++ b/litedram/frontend/adaptation.py @@ -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) ), ] diff --git a/test/common.py b/test/common.py index ffab27e..6f6e25c 100644 --- a/test/common.py +++ b/test/common.py @@ -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: diff --git a/test/test_adaptation.py b/test/test_adaptation.py index 67aa2f2..cad7569 100644 --- a/test/test_adaptation.py +++ b/test/test_adaptation.py @@ -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