diff --git a/test/common.py b/test/common.py index 78f5247..9fd8630 100644 --- a/test/common.py +++ b/test/common.py @@ -1,5 +1,6 @@ # This file is Copyright (c) 2016-2019 Florent Kermarrec # This file is Copyright (c) 2016 Tim 'mithro' Ansell +# This file is Copyright (c) 2020 Antmicro # License: BSD import random @@ -89,3 +90,200 @@ class DRAMMemory: yield yield dram_port.cmd.ready.eq(0) yield + +class MemoryTestDataMixin: + @property + def bist_test_data(self): + data = { + "8bit": dict( + base=2, + end=2 + 8, # (end - base) must be pow of 2 + length=5, + # 2 3 4 5 6 7=2+5 + expected=[0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00], + ), + "32bit": dict( + base=0x04, + end=0x04 + 8, + length=5 * 4, + expected=[ + 0x00000000, # 0x00 + 0x00000000, # 0x04 + 0x00000001, # 0x08 + 0x00000002, # 0x0c + 0x00000003, # 0x10 + 0x00000004, # 0x14 + 0x00000000, # 0x18 + 0x00000000, # 0x1c + ], + ), + "64bit": dict( + base=0x10, + end=0x10 + 8, + length=5 * 8, + expected=[ + 0x0000000000000000, # 0x00 + 0x0000000000000000, # 0x08 + 0x0000000000000000, # 0x10 + 0x0000000000000001, # 0x18 + 0x0000000000000002, # 0x20 + 0x0000000000000003, # 0x28 + 0x0000000000000004, # 0x30 + 0x0000000000000000, # 0x38 + ], + ), + "32bit_masked": dict( + base=0x04, + end=0x04 + 0x04, # TODO: fix address masking to be consistent + length=6 * 4, + expected=[ # due to masking + 0x00000000, # 0x00 + 0x00000004, # 0x04 + 0x00000005, # 0x08 + 0x00000002, # 0x0c + 0x00000003, # 0x10 + 0x00000000, # 0x14 + 0x00000000, # 0x18 + 0x00000000, # 0x1c + ], + ), + } + data["32bit_long_sequential"] = dict( + base=16, + end=16 + 128, + length=64, + expected=[0x00000000] * 128 + ) + expected = data["32bit_long_sequential"]["expected"] + expected[16//4:(16 + 64)//4] = list(range(64//4)) + return data + + @property + def pattern_test_data(self): + data = { + "8bit": dict( + pattern=[ + # address, data + (0x00, 0xaa), + (0x05, 0xbb), + (0x02, 0xcc), + (0x07, 0xdd), + ], + expected=[ + # data, address + 0xaa, # 0x00 + 0x00, # 0x01 + 0xcc, # 0x02 + 0x00, # 0x03 + 0x00, # 0x04 + 0xbb, # 0x05 + 0x00, # 0x06 + 0xdd, # 0x07 + ], + ), + "32bit": dict( + pattern=[ + # address, data + (0x00, 0xabadcafe), + (0x07, 0xbaadf00d), + (0x02, 0xcafefeed), + (0x01, 0xdeadc0de), + ], + expected=[ + # data, address + 0xabadcafe, # 0x00 + 0xdeadc0de, # 0x04 + 0xcafefeed, # 0x08 + 0x00000000, # 0x0c + 0x00000000, # 0x10 + 0x00000000, # 0x14 + 0x00000000, # 0x18 + 0xbaadf00d, # 0x1c + ], + ), + "64bit": dict( + pattern=[ + # address, data + (0x00, 0x0ddf00dbadc0ffee), + (0x05, 0xabadcafebaadf00d), + (0x02, 0xcafefeedfeedface), + (0x07, 0xdeadc0debaadbeef), + ], + expected=[ + # data, address + 0x0ddf00dbadc0ffee, # 0x00 + 0x0000000000000000, # 0x08 + 0xcafefeedfeedface, # 0x10 + 0x0000000000000000, # 0x18 + 0x0000000000000000, # 0x20 + 0xabadcafebaadf00d, # 0x28 + 0x0000000000000000, # 0x30 + 0xdeadc0debaadbeef, # 0x38 + ], + ), + "32bit_not_aligned": dict( + pattern=[ + # address, data + (0x00, 0xabadcafe), + (0x07, 0xbaadf00d), + (0x02, 0xcafefeed), + (0x01, 0xdeadc0de), + ], + expected=[ + # data, address + 0xabadcafe, # 0x00 + 0xdeadc0de, # 0x04 + 0xcafefeed, # 0x08 + 0x00000000, # 0x0c + 0x00000000, # 0x10 + 0x00000000, # 0x14 + 0x00000000, # 0x18 + 0xbaadf00d, # 0x1c + ], + ), + "32bit_duplicates": dict( + pattern=[ + # address, data + (0x00, 0xabadcafe), + (0x07, 0xbaadf00d), + (0x00, 0xcafefeed), + (0x07, 0xdeadc0de), + ], + expected=[ + # data, address + 0xcafefeed, # 0x00 + 0x00000000, # 0x04 + 0x00000000, # 0x08 + 0x00000000, # 0x0c + 0x00000000, # 0x10 + 0x00000000, # 0x14 + 0x00000000, # 0x18 + 0xdeadc0de, # 0x1c + ], + ), + "32bit_sequential": dict( + pattern=[ + # address, data + (0x02, 0xabadcafe), + (0x03, 0xbaadf00d), + (0x04, 0xcafefeed), + (0x05, 0xdeadc0de), + ], + expected=[ + # data, address + 0x00000000, # 0x00 + 0x00000000, # 0x04 + 0xabadcafe, # 0x08 + 0xbaadf00d, # 0x0c + 0xcafefeed, # 0x10 + 0xdeadc0de, # 0x14 + 0x00000000, # 0x18 + 0x00000000, # 0x1c + ], + ), + "32bit_long_sequential": dict(pattern=[], expected=[0] * 64), + } + for i in range(32): + data["32bit_long_sequential"]["pattern"].append((i, 64 + i)) + data["32bit_long_sequential"]["expected"][i] = 64 + i + return data diff --git a/test/test_bist.py b/test/test_bist.py index d74e5e1..b83dc0f 100644 --- a/test/test_bist.py +++ b/test/test_bist.py @@ -1,23 +1,21 @@ # This file is Copyright (c) 2016-2018 Florent Kermarrec # This file is Copyright (c) 2016 Tim 'mithro' Ansell +# This file is Copyright (c) 2020 Antmicro # License: BSD import unittest -import random from migen import * -from litex.soc.interconnect.stream import * +from litex.gen.sim import * from litedram.common import * from litedram.frontend.bist import * -from litedram.frontend.bist import _LiteDRAMBISTGenerator -from litedram.frontend.bist import _LiteDRAMBISTChecker +from litedram.frontend.bist import _LiteDRAMBISTGenerator, _LiteDRAMBISTChecker, \ + _LiteDRAMPatternGenerator, _LiteDRAMPatternChecker from test.common import * -from litex.gen.sim import * - class GenCheckDriver: def __init__(self, module): @@ -29,12 +27,19 @@ class GenCheckDriver: yield self.module.reset.eq(0) yield - def run(self, base, length, end=None): + def configure(self, base, length, end=None, random_addr=None, random_data=None): + # for non-pattern generators/checkers if end is None: end = base + 0x100000 yield self.module.base.eq(base) yield self.module.end.eq(end) yield self.module.length.eq(length) + if random_addr is not None: + yield self.module.random_addr.eq(random_addr) + if random_data is not None: + yield self.module.random_data.eq(random_data) + + def run(self): yield self.module.run.eq(1) yield self.module.start.eq(1) yield @@ -46,10 +51,43 @@ class GenCheckDriver: self.errors = (yield self.module.errors) -class TestBIST(unittest.TestCase): - def test_generator(self): - port = LiteDRAMNativeWritePort(address_width=32, data_width=32) +class GenCheckCSRDriver: + def __init__(self, module): + self.module = module + def reset(self): + yield from self.module.reset.write(1) + yield from self.module.reset.write(0) + + def configure(self, base, length, end=None, random_addr=None, random_data=None): + # for non-pattern generators/checkers + if end is None: + end = base + 0x100000 + yield from self.module.base.write(base) + yield from self.module.end.write(end) + yield from self.module.length.write(length) + if random_addr is not None: + yield from self.module.random.addr.write(random_addr) + if random_data is not None: + yield from self.module.random.data.write(random_data) + + def run(self): + yield from self.module.run.write(1) + yield from self.module.start.write(1) + yield + yield from self.module.start.write(0) + yield + while((yield from self.module.done.read()) == 0): + yield + if hasattr(self.module, "errors"): + self.errors = (yield from self.module.errors.read()) + + +class TestBIST(MemoryTestDataMixin, unittest.TestCase): + + # Generator ------------------------------------------------------------------------------------ + + def test_generator(self): def main_generator(dut): self.errors = 0 @@ -77,92 +115,231 @@ class TestBIST(unittest.TestCase): # dut dut = Generator(23, n_state=23, taps=[17, 22]) - # simulation + # simulation generators = [main_generator(dut)] run_simulation(dut, generators) self.assertEqual(self.errors, 0) - def bist_generator_test(self, data_width, base, length, end, mem_depth, init_generator=None): - end_addr = base + length - start_word = base // (data_width//8) - end_word = end_addr // (data_width//8) - n_words = end_word - start_word + def generator_test(self, mem_expected, data_width, pattern=None, config_args=None, + check_mem=True): + assert pattern is None or config_args is None, \ + "_LiteDRAMBISTGenerator xor _LiteDRAMPatternGenerator" class DUT(Module): def __init__(self): self.write_port = LiteDRAMNativeWritePort(address_width=32, data_width=data_width) - self.submodules.generator = _LiteDRAMBISTGenerator(self.write_port) - self.mem = DRAMMemory(data_width, mem_depth) + if pattern is not None: + self.submodules.generator = _LiteDRAMPatternGenerator(self.write_port, pattern) + else: + self.submodules.generator = _LiteDRAMBISTGenerator(self.write_port) + self.mem = DRAMMemory(data_width, len(mem_expected)) - def main_generator(dut): - generator = GenCheckDriver(dut.generator) - - if init_generator is not None: - yield from init_generator(dut) - - yield from generator.reset() - yield from generator.run(base, length, end=end) + def main_generator(driver): + yield from driver.reset() + if pattern is None: + yield from driver.configure(**config_args) + yield from driver.run() yield dut = DUT() - generators = [ - main_generator(dut), + main_generator(GenCheckDriver(dut.generator)), dut.mem.write_handler(dut.write_port), ] - return dut, generators - - def test_bist_generator(self): - dut, generators = self.bist_generator_test(mem_depth=128, data_width=32, end=128 * 4, - base=16, length=64) run_simulation(dut, generators) + if check_mem: + self.assertEqual(dut.mem.mem, mem_expected) + return dut - before = 16 // 4 - mem_expected = [0] * before + list(range(64//4)) + [0] * (128 - 64//4 - before) - self.assertEqual(dut.mem.mem, mem_expected) + # _LiteDRAMBISTGenerator ----------------------------------------------------------------------- + + def test_bist_generator_8bit(self): + data = self.bist_test_data["8bit"] + self.generator_test(data.pop("expected"), data_width=8, config_args=data) + + def test_bist_generator_range_must_be_pow2(self): + # NOTE: + # in the current implementation (end - start) must be a power of 2, + # but it would be better if this restriction didn't hold, this test + # is here just to notice the change if it happens unintentionally + # and may be removed if we start supporting arbitrary ranges + data = self.bist_test_data["8bit"] + data["end"] += 1 + reference = data.pop("expected") + dut = self.generator_test(reference, data_width=8, config_args=data, check_mem=False) + self.assertNotEqual(dut.mem.mem, reference) + + def test_bist_generator_32bit(self): + data = self.bist_test_data["32bit"] + self.generator_test(data.pop("expected"), data_width=32, config_args=data) + + def test_bist_generator_64bit(self): + data = self.bist_test_data["64bit"] + self.generator_test(data.pop("expected"), data_width=64, config_args=data) + + def test_bist_generator_32bit_address_masked(self): + data = self.bist_test_data["32bit_masked"] + self.generator_test(data.pop("expected"), data_width=32, config_args=data) + + def test_bist_generator_32bit_long_sequential(self): + data = self.bist_test_data["32bit_long_sequential"] + self.generator_test(data.pop("expected"), data_width=32, config_args=data) def test_bist_generator_random_data(self): - def init(dut): - yield dut.generator.random_data.eq(1) - yield - - # fill whole memory - dut, generators = self.bist_generator_test(mem_depth=128, data_width=32, end=128 * 4, - base=0, length=128 * 4, init_generator=init) - run_simulation(dut, generators) - - # only check if there are no duplicates and if data is not a simple sequence - self.assertEqual(len(set(dut.mem.mem)), len(dut.mem.mem), msg='Duplicate values in memory') - self.assertNotEqual(dut.mem.mem, list(range(128)), msg='Values are a sequence') - - def test_bist_generator_random_addr(self): # write whole memory and check if there are no repetitions? - def init(dut): - yield dut.generator.random_addr.eq(1) - yield - - # fill whole memory - dut, generators = self.bist_generator_test(mem_depth=128, data_width=32, end=128 * 4, - base=0, length=128 * 4, init_generator=init) - run_simulation(dut, generators) + data = self.bist_test_data["32bit"] + data["random_data"] = True + dut = self.generator_test(data.pop("expected"), data_width=32, config_args=data, + check_mem=False) + # only check that there are no duplicates and that data is not a simple sequence + mem = [val for val in dut.mem.mem if val != 0] + self.assertEqual(len(set(mem)), len(mem), msg="Duplicate values in memory") + self.assertNotEqual(mem, list(range(len(mem))), msg="Values are a sequence") + def test_bist_generator_random_addr(self): + data = self.bist_test_data["32bit"] + data["random_addr"] = True + dut = self.generator_test(data.pop("expected"), data_width=32, config_args=data, + check_mem=False) # with random address and address wrapping (generator.end) we _can_ have duplicates # we can at least check that the values written are not an ordered sequence - self.assertNotEqual(dut.mem.mem, list(range(128)), msg='Values are a sequence') + mem = [val for val in dut.mem.mem if val != 0] + self.assertNotEqual(mem, list(range(len(mem))), msg="Values are a sequence") + self.assertLess(max(mem), data["length"], msg="Too big value found") - def test_bist_generator_wraps_addr(self): - dut, generators = self.bist_generator_test(mem_depth=128, data_width=32, - base=16, length=96, end=32) + # _LiteDRAMPatternGenerator -------------------------------------------------------------------- + + def test_pattern_generator_8bit(self): + data = self.pattern_test_data["8bit"] + self.generator_test(data["expected"], data_width=8, pattern=data["pattern"]) + + def test_pattern_generator_32bit(self): + data = self.pattern_test_data["32bit"] + self.generator_test(data["expected"], data_width=32, pattern=data["pattern"]) + + def test_pattern_generator_64bit(self): + data = self.pattern_test_data["64bit"] + self.generator_test(data["expected"], data_width=64, pattern=data["pattern"]) + + def test_pattern_generator_32bit_not_aligned(self): + data = self.pattern_test_data["32bit_not_aligned"] + self.generator_test(data["expected"], data_width=32, pattern=data["pattern"]) + + def test_pattern_generator_32bit_duplicates(self): + data = self.pattern_test_data["32bit_duplicates"] + self.generator_test(data["expected"], data_width=32, pattern=data["pattern"]) + + def test_pattern_generator_32bit_sequential(self): + data = self.pattern_test_data["32bit_sequential"] + self.generator_test(data["expected"], data_width=32, pattern=data["pattern"]) + + # _LiteDRAMBISTChecker ------------------------------------------------------------------------- + + def checker_test(self, memory, data_width, pattern=None, config_args=None, check_errors=False): + assert pattern is None or config_args is None, \ + "_LiteDRAMBISTChecker xor _LiteDRAMPatternChecker" + + class DUT(Module): + def __init__(self): + self.read_port = LiteDRAMNativeReadPort(address_width=32, data_width=data_width) + if pattern is not None: + self.submodules.checker = _LiteDRAMPatternChecker(self.read_port, init=pattern) + else: + self.submodules.checker = _LiteDRAMBISTChecker(self.read_port) + self.mem = DRAMMemory(data_width, len(memory), init=memory) + + def main_generator(driver): + yield from driver.reset() + if pattern is None: + yield from driver.configure(**config_args) + yield from driver.run() + yield + + dut = DUT() + checker = GenCheckDriver(dut.checker) + generators = [ + main_generator(checker), + dut.mem.read_handler(dut.read_port), + ] run_simulation(dut, generators) + if check_errors: + self.assertEqual(checker.errors, 0) + return dut, checker - # we restrict address to <16, 32) and write 96 bytes (which results in 96/4=24 words generated) - # this means that the address should wrap and last 8 generated words should overwrite memory - # at address <16, 24) - before = 16 // 4 - mem_expected = [0] * 4 + list(range(16)) + [0] * (128 - 4 - 16) - mem_expected[4:4+8] = list(range(16, 24)) - self.assertEqual(dut.mem.mem, mem_expected) + def test_bist_checker_8bit(self): + data = self.bist_test_data["8bit"] + memory = data.pop("expected") + self.checker_test(memory, data_width=8, config_args=data) - def test_bist(self): + def test_bist_checker_32bit(self): + data = self.bist_test_data["32bit"] + memory = data.pop("expected") + self.checker_test(memory, data_width=32, config_args=data) + + def test_bist_checker_64bit(self): + data = self.bist_test_data["32bit"] + memory = data.pop("expected") + self.checker_test(memory, data_width=32, config_args=data) + + # _LiteDRAMPatternChecker ---------------------------------------------------------------------- + + def test_pattern_checker_8bit(self): + data = self.pattern_test_data["8bit"] + self.checker_test(memory=data["expected"], data_width=8, pattern=data["pattern"]) + + def test_pattern_checker_32bit(self): + data = self.pattern_test_data["32bit"] + self.checker_test(memory=data["expected"], data_width=32, pattern=data["pattern"]) + + def test_pattern_checker_64bit(self): + data = self.pattern_test_data["64bit"] + self.checker_test(memory=data["expected"], data_width=64, pattern=data["pattern"]) + + def test_pattern_checker_32bit_not_aligned(self): + data = self.pattern_test_data["32bit_not_aligned"] + self.checker_test(memory=data["expected"], data_width=32, pattern=data["pattern"]) + + def test_pattern_checker_32bit_duplicates(self): + data = self.pattern_test_data["32bit_duplicates"] + num_duplicates = len(data["pattern"]) - len(set(adr for adr, _ in data["pattern"])) + dut, checker = self.checker_test( + memory=data["expected"], data_width=32, pattern=data["pattern"], check_errors=False) + self.assertEqual(checker.errors, num_duplicates) + + # LiteDRAMBISTGenerator and LiteDRAMBISTChecker ------------------------------------------------ + + def bist_test(self, generator, checker, mem): + # write + yield from generator.reset() + yield from generator.configure(16, 64) + yield from generator.run() + + # read (no errors) + yield from checker.reset() + yield from checker.configure(16, 64) + yield from checker.run() + self.assertEqual(checker.errors, 0) + + # corrupt memory (using generator) + yield from generator.reset() + yield from generator.configure(16 + 48, 64) + yield from generator.run() + + # read (errors) + yield from checker.reset() + yield from checker.configure(16, 64) + yield from checker.run() + # errors for words: + # from (16 + 48) / 4 = 16 (corrupting generator start) + # to (16 + 64) / 4 = 20 (first generator end) + self.assertEqual(checker.errors, 4) + + # read (no errors) + yield from checker.reset() + yield from checker.configure(16 + 48, 64) + yield from checker.run() + self.assertEqual(checker.errors, 0) + + def test_bist_base(self): class DUT(Module): def __init__(self): self.write_port = LiteDRAMNativeWritePort(address_width=32, data_width=32) @@ -173,38 +350,75 @@ class TestBIST(unittest.TestCase): def main_generator(dut, mem): generator = GenCheckDriver(dut.generator) checker = GenCheckDriver(dut.checker) - - # write - yield from generator.reset() - yield from generator.run(16, 64) - - # read (no errors) - yield from checker.reset() - yield from checker.run(16, 64) - assert checker.errors == 0 - - # corrupt memory (using generator) - yield from generator.reset() - yield from generator.run(16 + 60, 64) - - # read (4 errors) - yield from checker.reset() - yield from checker.run(16, 64) - assert checker.errors != 0 - - # read (no errors) - yield from checker.reset() - yield from checker.run(16 + 60, 64) - assert checker.errors == 0 + yield from self.bist_test(generator, checker, mem) # dut dut = DUT() - mem = DRAMMemory(32, 128) + mem = DRAMMemory(32, 48) # simulation generators = [ main_generator(dut, mem), mem.write_handler(dut.write_port), mem.read_handler(dut.read_port) - ] + ] run_simulation(dut, generators) + + def test_bist_csr(self): + class DUT(Module): + def __init__(self): + self.write_port = LiteDRAMNativeWritePort(address_width=32, data_width=32) + self.read_port = LiteDRAMNativeReadPort(address_width=32, data_width=32) + self.submodules.generator = LiteDRAMBISTGenerator(self.write_port) + self.submodules.checker = LiteDRAMBISTChecker(self.read_port) + + def main_generator(dut, mem): + generator = GenCheckCSRDriver(dut.generator) + checker = GenCheckCSRDriver(dut.checker) + yield from self.bist_test(generator, checker, mem) + + # dut + dut = DUT() + mem = DRAMMemory(32, 48) + + # simulation + generators = [ + main_generator(dut, mem), + mem.write_handler(dut.write_port), + mem.read_handler(dut.read_port) + ] + run_simulation(dut, generators) + + # FIXME: synchronization between CSRs: `start` and `base`, `done` and `errors` + # def test_bist_csr_cdc(self): + # class DUT(Module): + # def __init__(self): + # port_kwargs = dict(address_width=32, data_width=32, clock_domain="async") + # self.write_port = LiteDRAMNativeWritePort(**port_kwargs) + # self.read_port = LiteDRAMNativeReadPort(**port_kwargs) + # self.submodules.generator = LiteDRAMBISTGenerator(self.write_port) + # self.submodules.checker = LiteDRAMBISTChecker(self.read_port) + # + # def main_generator(dut, mem): + # generator = GenCheckCSRDriver(dut.generator) + # checker = GenCheckCSRDriver(dut.checker) + # yield from self.bist_test(generator, checker, mem) + # + # # dut + # dut = DUT() + # mem = DRAMMemory(32, 48) + # + # generators = { + # "sys": [ + # main_generator(dut, mem), + # ], + # "async": [ + # mem.write_handler(dut.write_port), + # mem.read_handler(dut.read_port) + # ] + # } + # clocks = { + # "sys": 10, + # "async": (7, 3), + # } + # run_simulation(dut, generators, clocks) diff --git a/test/test_dma.py b/test/test_dma.py new file mode 100644 index 0000000..c306ef7 --- /dev/null +++ b/test/test_dma.py @@ -0,0 +1,165 @@ +# This file is Copyright (c) 2020 Antmicro +# License: BSD + +import unittest + +from migen import * + +from litex.gen.sim import * + +from litedram.common import * +from litedram.frontend.dma import * + +from test.common import * + + +class DMAWriterDriver: + def __init__(self, dma): + self.dma = dma + + def write(self, pattern): + yield self.dma.sink.valid.eq(1) + for adr, data in pattern: + yield self.dma.sink.address.eq(adr) + yield self.dma.sink.data.eq(data) + while not (yield self.dma.sink.ready): + yield + yield + yield self.dma.sink.valid.eq(0) + + @staticmethod + def wait_complete(port, n): + for _ in range(n): + while not (yield port.wdata.ready): + yield + yield + + +class DMAReaderDriver: + def __init__(self, dma): + self.dma = dma + self.data = [] + + def read(self, address_list): + n_last = len(self.data) + yield self.dma.sink.valid.eq(1) + for adr in address_list: + yield self.dma.sink.address.eq(adr) + while not (yield self.dma.sink.ready): + yield + while (yield self.dma.sink.ready): + yield + yield self.dma.sink.valid.eq(0) + while len(self.data) < n_last + len(address_list): + yield + + @passive + def read_handler(self): + yield self.dma.source.ready.eq(1) + while True: + if (yield self.dma.source.valid): + self.data.append((yield self.dma.source.data)) + yield + + +class TestDMA(MemoryTestDataMixin, unittest.TestCase): + + # LiteDRAMDMAWriter ---------------------------------------------------------------------------- + + def dma_writer_test(self, pattern, mem_expected, data_width, **kwargs): + class DUT(Module): + def __init__(self): + self.port = LiteDRAMNativeWritePort(address_width=32, data_width=data_width) + self.submodules.dma = LiteDRAMDMAWriter(self.port, **kwargs) + + dut = DUT() + driver = DMAWriterDriver(dut.dma) + mem = DRAMMemory(data_width, len(mem_expected)) + + generators = [ + driver.write(pattern), + driver.wait_complete(dut.port, len(pattern)), + mem.write_handler(dut.port), + ] + run_simulation(dut, generators) + self.assertEqual(mem.mem, mem_expected) + + def test_dma_writer_single(self): + pattern = [(0x04, 0xdeadc0de)] + mem_expected = [0] * 32 + mem_expected[0x04] = 0xdeadc0de + self.dma_writer_test(pattern, mem_expected, data_width=32) + + def test_dma_writer_multiple(self): + data = self.pattern_test_data["32bit"] + self.dma_writer_test(data["pattern"], data["expected"], data_width=32) + + def test_dma_writer_sequential(self): + data = self.pattern_test_data["32bit_sequential"] + self.dma_writer_test(data["pattern"], data["expected"], data_width=32) + + def test_dma_writer_long_sequential(self): + data = self.pattern_test_data["32bit_long_sequential"] + self.dma_writer_test(data["pattern"], data["expected"], data_width=32) + + def test_dma_writer_no_fifo(self): + data = self.pattern_test_data["32bit_long_sequential"] + self.dma_writer_test(data["pattern"], data["expected"], data_width=32, + fifo_depth=1) + + def test_dma_writer_fifo_buffered(self): + data = self.pattern_test_data["32bit_long_sequential"] + self.dma_writer_test(data["pattern"], data["expected"], data_width=32, + fifo_buffered=True) + + def test_dma_writer_duplicates(self): + data = self.pattern_test_data["32bit_duplicates"] + self.dma_writer_test(data["pattern"], data["expected"], data_width=32) + + # LiteDRAMDMAReader ---------------------------------------------------------------------------- + + def dma_reader_test(self, pattern, mem_expected, data_width, **kwargs): + class DUT(Module): + def __init__(self): + self.port = LiteDRAMNativeReadPort(address_width=32, data_width=data_width) + self.submodules.dma = LiteDRAMDMAReader(self.port, **kwargs) + + dut = DUT() + driver = DMAReaderDriver(dut.dma) + mem = DRAMMemory(data_width, len(mem_expected), init=mem_expected) + + generators = [ + driver.read([adr for adr, data in pattern]), + driver.read_handler(), + mem.read_handler(dut.port), + ] + run_simulation(dut, generators) + self.assertEqual(driver.data, [data for adr, data in pattern]) + + def test_dma_reader_single(self): + pattern = [(0x04, 0xdeadc0de)] + mem_expected = [0] * 32 + mem_expected[0x04] = 0xdeadc0de + self.dma_reader_test(pattern, mem_expected, data_width=32) + + def test_dma_reader_multiple(self): + data = self.pattern_test_data["32bit"] + self.dma_reader_test(data["pattern"], data["expected"], data_width=32) + + def test_dma_reader_sequential(self): + data = self.pattern_test_data["32bit_sequential"] + self.dma_reader_test(data["pattern"], data["expected"], data_width=32) + + def test_dma_reader_long_sequential(self): + data = self.pattern_test_data["32bit_long_sequential"] + self.dma_reader_test(data["pattern"], data["expected"], data_width=32) + + def test_dma_reader_no_fifo(self): + data = self.pattern_test_data["32bit_long_sequential"] + self.dma_reader_test(data["pattern"], data["expected"], data_width=32, + fifo_depth=1) + + def test_dma_reader_fifo_buffered(self): + data = self.pattern_test_data["32bit_long_sequential"] + self.dma_reader_test(data["pattern"], data["expected"], data_width=32, + fifo_buffered=True)