From ac06382b5aafc2f2eef8599ab9cfa4b7c8f72ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Fri, 13 Mar 2020 09:58:02 +0100 Subject: [PATCH 01/12] test: split GenCheckDriver run into configure/run --- test/test_bist.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/test/test_bist.py b/test/test_bist.py index d74e5e1..372fa8e 100644 --- a/test/test_bist.py +++ b/test/test_bist.py @@ -29,12 +29,15 @@ class GenCheckDriver: yield self.module.reset.eq(0) yield - def run(self, base, length, end=None): + def configure(self, base, length, end=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) + + def run(self): yield self.module.run.eq(1) yield self.module.start.eq(1) yield @@ -101,7 +104,8 @@ class TestBIST(unittest.TestCase): yield from init_generator(dut) yield from generator.reset() - yield from generator.run(base, length, end=end) + yield from generator.configure(base, length, end=end) + yield from generator.run() yield dut = DUT() @@ -176,25 +180,30 @@ class TestBIST(unittest.TestCase): # write yield from generator.reset() - yield from generator.run(16, 64) + yield from generator.configure(16, 64) + yield from generator.run() # read (no errors) yield from checker.reset() - yield from checker.run(16, 64) + yield from checker.configure(16, 64) + yield from checker.run() assert checker.errors == 0 # corrupt memory (using generator) yield from generator.reset() - yield from generator.run(16 + 60, 64) + yield from generator.configure(16 + 60, 64) + yield from generator.run() # read (4 errors) yield from checker.reset() - yield from checker.run(16, 64) + yield from checker.configure(16, 64) + yield from checker.run() assert checker.errors != 0 # read (no errors) yield from checker.reset() - yield from checker.run(16 + 60, 64) + yield from checker.configure(16 + 60, 64) + yield from checker.run() assert checker.errors == 0 # dut From 239859d95b449bcda2a68e711e2438086e7386c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Thu, 12 Mar 2020 17:04:22 +0100 Subject: [PATCH 02/12] test: add tests for _LiteDRAMPatternGenerator --- test/test_bist.py | 160 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 2 deletions(-) diff --git a/test/test_bist.py b/test/test_bist.py index 372fa8e..0fdf1cf 100644 --- a/test/test_bist.py +++ b/test/test_bist.py @@ -11,8 +11,8 @@ from litex.soc.interconnect.stream 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 * @@ -166,6 +166,162 @@ class TestBIST(unittest.TestCase): mem_expected[4:4+8] = list(range(16, 24)) self.assertEqual(dut.mem.mem, mem_expected) + def pattern_generator_test(self, pattern, mem_expected, data_width, mem_depth): + class DUT(Module): + def __init__(self, init): + self.write_port = LiteDRAMNativeWritePort(address_width=32, data_width=data_width) + self.submodules.generator = _LiteDRAMPatternGenerator(self.write_port, init=init) + self.mem = DRAMMemory(data_width, mem_depth) + + def main_generator(dut): + generator = GenCheckDriver(dut.generator) + + yield from generator.reset() + yield from generator.run() + yield + + dut = DUT(init=pattern) + + generators = [ + main_generator(dut), + dut.mem.write_handler(dut.write_port), + ] + run_simulation(dut, generators, vcd_name='/tmp/sim.vcd') + + assert len(mem_expected) == mem_depth + self.assertEqual(dut.mem.mem, mem_expected) + + def test_pattern_generator_8bit(self): + 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 + ] + self.pattern_generator_test(pattern, expected, data_width=8, mem_depth=8) + + def test_pattern_generator_64bit(self): + 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 + ] + self.pattern_generator_test(pattern, expected, data_width=64, mem_depth=8) + + def test_pattern_generator_aligned(self): + 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 + ] + self.pattern_generator_test(pattern, expected, data_width=32, mem_depth=8) + + def test_pattern_generator_not_aligned(self): + 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 + ] + self.pattern_generator_test(pattern, expected, data_width=32, mem_depth=8) + + def test_pattern_generator_overwriting(self): + 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 + ] + self.pattern_generator_test(pattern, expected, data_width=32, mem_depth=8) + + def test_pattern_generator_sequential(self): + length = 64 + prng = random.Random(42) + address = [a for a in range(length)] + data = prng.choices(range(2**32 - 1), k=length) + pattern = list(zip(address, data)) + + expected = [0x00000000] * 128 + for adr, data in pattern: + expected[adr] = data + + self.pattern_generator_test(pattern, expected, data_width=32, mem_depth=128) + + def test_pattern_generator_random(self): + length = 64 + prng = random.Random(42) + address = [a for a in prng.sample(range(128), k=length)] + data = prng.choices(range(2**32 - 1), k=length) + pattern = list(zip(address, data)) + + expected = [0x00000000] * 128 + for adr, data in pattern: + expected[adr] = data + + self.pattern_generator_test(pattern, expected, data_width=32, mem_depth=128) + def test_bist(self): class DUT(Module): def __init__(self): From ba83e5645c34fc15a390b08f46173f5e9350ad13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Fri, 13 Mar 2020 11:58:26 +0100 Subject: [PATCH 03/12] test: add some more verbose _LiteDRAMBISTGenerator tests --- test/test_bist.py | 78 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 14 deletions(-) diff --git a/test/test_bist.py b/test/test_bist.py index 0fdf1cf..9fba194 100644 --- a/test/test_bist.py +++ b/test/test_bist.py @@ -125,6 +125,69 @@ class TestBIST(unittest.TestCase): mem_expected = [0] * before + list(range(64//4)) + [0] * (128 - 64//4 - before) self.assertEqual(dut.mem.mem, mem_expected) + def test_bist_generator_8bit(self): + expected = [0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00] + dut, generators = self.bist_generator_test(mem_depth=len(expected), data_width=8, + base=2, end=2 + 8, length=5) + run_simulation(dut, generators) + self.assertEqual(dut.mem.mem, expected) + + 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 + expected = [0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00] + dut, generators = self.bist_generator_test(mem_depth=len(expected), data_width=8, + base=2, end=2 + 6, length=5) + run_simulation(dut, generators) + self.assertNotEqual(dut.mem.mem, expected) + + def test_bist_generator_64bit(self): + expected = [ + 0x0000000000000000, # 0x00 + 0x0000000000000000, # 0x08 + 0x0000000000000000, # 0x10 + 0x0000000000000001, # 0x18 + 0x0000000000000002, # 0x20 + 0x0000000000000003, # 0x28 + 0x0000000000000004, # 0x30 + 0x0000000000000000, # 0x38 + ] + dut, generators = self.bist_generator_test(mem_depth=len(expected), data_width=64, + base=0x10, end=0x10 + 0x20, length=5 * 8) + run_simulation(dut, generators) + self.assertEqual(dut.mem.mem, expected) + + def test_bist_generator_address_masked(self): + expected = [ + 0x00000000, # 0x00 + 0x00000004, # 0x04 + 0x00000005, # 0x08 + 0x00000002, # 0x0c + 0x00000003, # 0x10 + 0x00000000, # 0x14 + 0x00000000, # 0x18 + 0x00000000, # 0x1c + ] + dut, generators = self.bist_generator_test(mem_depth=len(expected), data_width=32, + base=0x04, end=0x04 + 0x04, length=6 * 4) + run_simulation(dut, generators, vcd_name='/tmp/sim.vcd') + self.assertEqual(dut.mem.mem, expected) + + def test_bist_generator_address_masked_long(self): + dut, generators = self.bist_generator_test(mem_depth=128, data_width=32, + base=16, length=96, end=32) + run_simulation(dut, generators) + + # 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_generator_random_data(self): def init(dut): yield dut.generator.random_data.eq(1) @@ -153,19 +216,6 @@ class TestBIST(unittest.TestCase): # 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') - 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) - run_simulation(dut, generators) - - # 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 pattern_generator_test(self, pattern, mem_expected, data_width, mem_depth): class DUT(Module): def __init__(self, init): @@ -186,7 +236,7 @@ class TestBIST(unittest.TestCase): main_generator(dut), dut.mem.write_handler(dut.write_port), ] - run_simulation(dut, generators, vcd_name='/tmp/sim.vcd') + run_simulation(dut, generators) assert len(mem_expected) == mem_depth self.assertEqual(dut.mem.mem, mem_expected) From 13aeb3fd6546de8f10884242bce617bfd2b22650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Fri, 13 Mar 2020 13:58:01 +0100 Subject: [PATCH 04/12] test: add _LiteDRAMBISTChecker/_LiteDRAMPatternChecker tests --- test/test_bist.py | 452 +++++++++++++++++++++++++++++++--------------- 1 file changed, 306 insertions(+), 146 deletions(-) diff --git a/test/test_bist.py b/test/test_bist.py index 9fba194..a42eaf6 100644 --- a/test/test_bist.py +++ b/test/test_bist.py @@ -50,6 +50,189 @@ class GenCheckDriver: class TestBIST(unittest.TestCase): + def setUp(self): + # define common test data used for both generator and checker tests + self.bist_test_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, # TODO: fix address masking to be consistent + 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, + length = 6 * 4, + expected = [ + 0x00000000, # 0x00 + 0x00000004, # 0x04 + 0x00000005, # 0x08 + 0x00000002, # 0x0c + 0x00000003, # 0x10 + 0x00000000, # 0x14 + 0x00000000, # 0x18 + 0x00000000, # 0x1c + ], + ), + '32bit_long_sequential': dict( + base = 0x04, + end = 0x04 + 0x04, + length = 6 * 4, + expected = [ + 0x00000000, # 0x00 + 0x00000004, # 0x04 + 0x00000005, # 0x08 + 0x00000002, # 0x0c + 0x00000003, # 0x10 + 0x00000000, # 0x14 + 0x00000000, # 0x18 + 0x00000000, # 0x1c + ], + ), + } + self.bist_test_data['32bit_long_sequential'] = dict( + base = 16, + end = 16 + 128, + length = 64, + expected = [0x00000000] * 128 + ) + expected = self.bist_test_data['32bit_long_sequential']['expected'] + expected[16//4:(16 + 64)//4] = list(range(64//4)) + + self.pattern_test_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 + ], + ), + } + def test_generator(self): port = LiteDRAMNativeWritePort(address_width=32, data_width=32) @@ -116,64 +299,58 @@ class TestBIST(unittest.TestCase): ] 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) - - before = 16 // 4 - mem_expected = [0] * before + list(range(64//4)) + [0] * (128 - 64//4 - before) - self.assertEqual(dut.mem.mem, mem_expected) - def test_bist_generator_8bit(self): - expected = [0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00] - dut, generators = self.bist_generator_test(mem_depth=len(expected), data_width=8, - base=2, end=2 + 8, length=5) + data = self.bist_test_data['8bit'] + dut, generators = self.bist_generator_test( + mem_depth=len(data['expected']), data_width=8, + base=data['base'], end=data['end'], length=data['length']) run_simulation(dut, generators) - self.assertEqual(dut.mem.mem, expected) + self.assertEqual(dut.mem.mem, data['expected']) 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 - expected = [0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00] - dut, generators = self.bist_generator_test(mem_depth=len(expected), data_width=8, - base=2, end=2 + 6, length=5) + # 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'] + dut, generators = self.bist_generator_test( + mem_depth=len(data['expected']), data_width=8, + base=data['base'], end=data['end'] + 1, length=data['length']) run_simulation(dut, generators) - self.assertNotEqual(dut.mem.mem, expected) + self.assertNotEqual(dut.mem.mem, data['expected']) + + def test_bist_generator_32bit(self): + data = self.bist_test_data['32bit'] + dut, generators = self.bist_generator_test( + mem_depth=len(data['expected']), data_width=32, + base=data['base'], end=data['end'], length=data['length']) + run_simulation(dut, generators) + self.assertEqual(dut.mem.mem, data['expected']) def test_bist_generator_64bit(self): - expected = [ - 0x0000000000000000, # 0x00 - 0x0000000000000000, # 0x08 - 0x0000000000000000, # 0x10 - 0x0000000000000001, # 0x18 - 0x0000000000000002, # 0x20 - 0x0000000000000003, # 0x28 - 0x0000000000000004, # 0x30 - 0x0000000000000000, # 0x38 - ] - dut, generators = self.bist_generator_test(mem_depth=len(expected), data_width=64, - base=0x10, end=0x10 + 0x20, length=5 * 8) + data = self.bist_test_data['64bit'] + dut, generators = self.bist_generator_test( + mem_depth=len(data['expected']), data_width=64, + base=data['base'], end=data['end'], length=data['length']) run_simulation(dut, generators) - self.assertEqual(dut.mem.mem, expected) + self.assertEqual(dut.mem.mem, data['expected']) - def test_bist_generator_address_masked(self): - expected = [ - 0x00000000, # 0x00 - 0x00000004, # 0x04 - 0x00000005, # 0x08 - 0x00000002, # 0x0c - 0x00000003, # 0x10 - 0x00000000, # 0x14 - 0x00000000, # 0x18 - 0x00000000, # 0x1c - ] - dut, generators = self.bist_generator_test(mem_depth=len(expected), data_width=32, - base=0x04, end=0x04 + 0x04, length=6 * 4) - run_simulation(dut, generators, vcd_name='/tmp/sim.vcd') - self.assertEqual(dut.mem.mem, expected) + def test_bist_generator_32bit_address_masked(self): + data = self.bist_test_data['32bit_masked'] + dut, generators = self.bist_generator_test( + mem_depth=len(data['expected']), data_width=32, + base=data['base'], end=data['end'], length=data['length']) + run_simulation(dut, generators) + self.assertEqual(dut.mem.mem, data['expected']) + + def test_bist_generator_32bit_long_sequential(self): + data = self.bist_test_data['32bit_long_sequential'] + dut, generators = self.bist_generator_test( + mem_depth=len(data['expected']), data_width=32, + base=data['base'], end=data['end'], length=data['length']) + run_simulation(dut, generators) + self.assertEqual(dut.mem.mem, data['expected']) def test_bist_generator_address_masked_long(self): dut, generators = self.bist_generator_test(mem_depth=128, data_width=32, @@ -242,109 +419,24 @@ class TestBIST(unittest.TestCase): self.assertEqual(dut.mem.mem, mem_expected) def test_pattern_generator_8bit(self): - 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 - ] - self.pattern_generator_test(pattern, expected, data_width=8, mem_depth=8) + data = self.pattern_test_data['8bit'] + self.pattern_generator_test(data['pattern'], data['expected'], data_width=8, mem_depth=len(data['expected'])) def test_pattern_generator_64bit(self): - 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 - ] - self.pattern_generator_test(pattern, expected, data_width=64, mem_depth=8) + data = self.pattern_test_data['64bit'] + self.pattern_generator_test(data['pattern'], data['expected'], data_width=64, mem_depth=len(data['expected'])) - def test_pattern_generator_aligned(self): - 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 - ] - self.pattern_generator_test(pattern, expected, data_width=32, mem_depth=8) + def test_pattern_generator_32bit(self): + data = self.pattern_test_data['32bit'] + self.pattern_generator_test(data['pattern'], data['expected'], data_width=32, mem_depth=len(data['expected'])) def test_pattern_generator_not_aligned(self): - 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 - ] - self.pattern_generator_test(pattern, expected, data_width=32, mem_depth=8) + data = self.pattern_test_data['32bit_not_aligned'] + self.pattern_generator_test(data['pattern'], data['expected'], data_width=32, mem_depth=len(data['expected'])) - def test_pattern_generator_overwriting(self): - 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 - ] - self.pattern_generator_test(pattern, expected, data_width=32, mem_depth=8) + def test_pattern_generator_duplicates(self): + data = self.pattern_test_data['32bit_duplicates'] + self.pattern_generator_test(data['pattern'], data['expected'], data_width=32, mem_depth=len(data['expected'])) def test_pattern_generator_sequential(self): length = 64 @@ -372,6 +464,74 @@ class TestBIST(unittest.TestCase): self.pattern_generator_test(pattern, expected, data_width=32, mem_depth=128) + def bist_checker_test(self, memory, data_width, pattern=None, config_args=None, expected_errors=0): + 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(dut): + + yield from dut.reset() + if pattern is None: + yield from dut.configure(**config_args) + yield from dut.run() + yield + + dut = DUT() + checker = GenCheckDriver(dut.checker) + + generators = [ + main_generator(checker), + dut.mem.read_handler(dut.read_port), + ] + run_simulation(dut, generators, vcd_name='/tmp/sim.vcd') + self.assertEqual(checker.errors, expected_errors) + + + def test_bist_checker_8bit(self): + data = self.bist_test_data['8bit'] + memory = data.pop('expected') + self.bist_checker_test(memory, data_width=8, config_args=data) + + def test_bist_checker_32bit(self): + data = self.bist_test_data['32bit'] + memory = data.pop('expected') + self.bist_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.bist_checker_test(memory, data_width=32, config_args=data) + + def test_pattern_checker_8bit(self): + data = self.pattern_test_data['8bit'] + self.bist_checker_test(memory=data['expected'], data_width=8, pattern=data['pattern']) + + def test_pattern_checker_32bit(self): + data = self.pattern_test_data['32bit'] + self.bist_checker_test(memory=data['expected'], data_width=32, pattern=data['pattern']) + + def test_pattern_checker_64bit(self): + data = self.pattern_test_data['64bit'] + self.bist_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.bist_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'])) + self.bist_checker_test(memory=data['expected'], data_width=32, pattern=data['pattern'], + expected_errors=num_duplicates) + def test_bist(self): class DUT(Module): def __init__(self): From a00c8b79400c82362906b1d49702df0c2bbac3a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Fri, 13 Mar 2020 16:13:36 +0100 Subject: [PATCH 05/12] test: unify BIST tests, factor out repetitive code --- test/test_bist.py | 280 ++++++++++++++++------------------------------ 1 file changed, 98 insertions(+), 182 deletions(-) diff --git a/test/test_bist.py b/test/test_bist.py index a42eaf6..f9e59ae 100644 --- a/test/test_bist.py +++ b/test/test_bist.py @@ -29,13 +29,17 @@ class GenCheckDriver: yield self.module.reset.eq(0) yield - def configure(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) @@ -77,7 +81,7 @@ class TestBIST(unittest.TestCase): ), '64bit': dict( base = 0x10, - end = 0x10 + 8, # TODO: fix address masking to be consistent + end = 0x10 + 8, length = 5 * 8, expected = [ 0x0000000000000000, # 0x00 @@ -92,24 +96,9 @@ class TestBIST(unittest.TestCase): ), '32bit_masked': dict( base = 0x04, - end = 0x04 + 0x04, + end = 0x04 + 0x04, # TODO: fix address masking to be consistent length = 6 * 4, - expected = [ - 0x00000000, # 0x00 - 0x00000004, # 0x04 - 0x00000005, # 0x08 - 0x00000002, # 0x0c - 0x00000003, # 0x10 - 0x00000000, # 0x14 - 0x00000000, # 0x18 - 0x00000000, # 0x1c - ], - ), - '32bit_long_sequential': dict( - base = 0x04, - end = 0x04 + 0x04, - length = 6 * 4, - expected = [ + expected = [ # due to masking 0x00000000, # 0x00 0x00000004, # 0x04 0x00000005, # 0x08 @@ -231,6 +220,26 @@ class TestBIST(unittest.TestCase): 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 + ], + ), } def test_generator(self): @@ -268,44 +277,38 @@ class TestBIST(unittest.TestCase): 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, init=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.configure(base, length, end=end) - yield from generator.run() + 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 + run_simulation(dut, generators) + if check_mem: + self.assertEqual(dut.mem.mem, mem_expected) + return dut def test_bist_generator_8bit(self): data = self.bist_test_data['8bit'] - dut, generators = self.bist_generator_test( - mem_depth=len(data['expected']), data_width=8, - base=data['base'], end=data['end'], length=data['length']) - run_simulation(dut, generators) - self.assertEqual(dut.mem.mem, data['expected']) + self.generator_test(data.pop('expected'), data_width=8, config_args=data) def test_bist_generator_range_must_be_pow2(self): # NOTE: @@ -314,157 +317,71 @@ class TestBIST(unittest.TestCase): # 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'] - dut, generators = self.bist_generator_test( - mem_depth=len(data['expected']), data_width=8, - base=data['base'], end=data['end'] + 1, length=data['length']) - run_simulation(dut, generators) - self.assertNotEqual(dut.mem.mem, data['expected']) + 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'] - dut, generators = self.bist_generator_test( - mem_depth=len(data['expected']), data_width=32, - base=data['base'], end=data['end'], length=data['length']) - run_simulation(dut, generators) - self.assertEqual(dut.mem.mem, data['expected']) + self.generator_test(data.pop('expected'), data_width=32, config_args=data) def test_bist_generator_64bit(self): data = self.bist_test_data['64bit'] - dut, generators = self.bist_generator_test( - mem_depth=len(data['expected']), data_width=64, - base=data['base'], end=data['end'], length=data['length']) - run_simulation(dut, generators) - self.assertEqual(dut.mem.mem, data['expected']) + 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'] - dut, generators = self.bist_generator_test( - mem_depth=len(data['expected']), data_width=32, - base=data['base'], end=data['end'], length=data['length']) - run_simulation(dut, generators) - self.assertEqual(dut.mem.mem, data['expected']) + 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'] - dut, generators = self.bist_generator_test( - mem_depth=len(data['expected']), data_width=32, - base=data['base'], end=data['end'], length=data['length']) - run_simulation(dut, generators) - self.assertEqual(dut.mem.mem, data['expected']) - - def test_bist_generator_address_masked_long(self): - dut, generators = self.bist_generator_test(mem_depth=128, data_width=32, - base=16, length=96, end=32) - run_simulation(dut, generators) - - # 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) + 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') + 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): # 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_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') - - def pattern_generator_test(self, pattern, mem_expected, data_width, mem_depth): - class DUT(Module): - def __init__(self, init): - self.write_port = LiteDRAMNativeWritePort(address_width=32, data_width=data_width) - self.submodules.generator = _LiteDRAMPatternGenerator(self.write_port, init=init) - self.mem = DRAMMemory(data_width, mem_depth) - - def main_generator(dut): - generator = GenCheckDriver(dut.generator) - - yield from generator.reset() - yield from generator.run() - yield - - dut = DUT(init=pattern) - - generators = [ - main_generator(dut), - dut.mem.write_handler(dut.write_port), - ] - run_simulation(dut, generators) - - assert len(mem_expected) == mem_depth - self.assertEqual(dut.mem.mem, mem_expected) + 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_pattern_generator_8bit(self): data = self.pattern_test_data['8bit'] - self.pattern_generator_test(data['pattern'], data['expected'], data_width=8, mem_depth=len(data['expected'])) - - def test_pattern_generator_64bit(self): - data = self.pattern_test_data['64bit'] - self.pattern_generator_test(data['pattern'], data['expected'], data_width=64, mem_depth=len(data['expected'])) + self.generator_test(data['expected'], data_width=8, pattern=data['pattern']) def test_pattern_generator_32bit(self): data = self.pattern_test_data['32bit'] - self.pattern_generator_test(data['pattern'], data['expected'], data_width=32, mem_depth=len(data['expected'])) + self.generator_test(data['expected'], data_width=32, pattern=data['pattern']) - def test_pattern_generator_not_aligned(self): + 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.pattern_generator_test(data['pattern'], data['expected'], data_width=32, mem_depth=len(data['expected'])) + self.generator_test(data['expected'], data_width=32, pattern=data['pattern']) - def test_pattern_generator_duplicates(self): + def test_pattern_generator_32bit_duplicates(self): data = self.pattern_test_data['32bit_duplicates'] - self.pattern_generator_test(data['pattern'], data['expected'], data_width=32, mem_depth=len(data['expected'])) + self.generator_test(data['expected'], data_width=32, pattern=data['pattern']) - def test_pattern_generator_sequential(self): - length = 64 - prng = random.Random(42) - address = [a for a in range(length)] - data = prng.choices(range(2**32 - 1), k=length) - pattern = list(zip(address, data)) + 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']) - expected = [0x00000000] * 128 - for adr, data in pattern: - expected[adr] = data - - self.pattern_generator_test(pattern, expected, data_width=32, mem_depth=128) - - def test_pattern_generator_random(self): - length = 64 - prng = random.Random(42) - address = [a for a in prng.sample(range(128), k=length)] - data = prng.choices(range(2**32 - 1), k=length) - pattern = list(zip(address, data)) - - expected = [0x00000000] * 128 - for adr, data in pattern: - expected[adr] = data - - self.pattern_generator_test(pattern, expected, data_width=32, mem_depth=128) - - def bist_checker_test(self, memory, data_width, pattern=None, config_args=None, expected_errors=0): + 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): @@ -476,61 +393,60 @@ class TestBIST(unittest.TestCase): self.submodules.checker = _LiteDRAMBISTChecker(self.read_port) self.mem = DRAMMemory(data_width, len(memory), init=memory) - def main_generator(dut): - - yield from dut.reset() + def main_generator(driver): + yield from driver.reset() if pattern is None: - yield from dut.configure(**config_args) - yield from dut.run() + 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, vcd_name='/tmp/sim.vcd') - self.assertEqual(checker.errors, expected_errors) - + run_simulation(dut, generators) + if check_errors: + self.assertEqual(checker.errors, 0) + return dut, checker def test_bist_checker_8bit(self): data = self.bist_test_data['8bit'] memory = data.pop('expected') - self.bist_checker_test(memory, data_width=8, config_args=data) + self.checker_test(memory, data_width=8, config_args=data) def test_bist_checker_32bit(self): data = self.bist_test_data['32bit'] memory = data.pop('expected') - self.bist_checker_test(memory, data_width=32, config_args=data) + 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.bist_checker_test(memory, data_width=32, config_args=data) + self.checker_test(memory, data_width=32, config_args=data) def test_pattern_checker_8bit(self): data = self.pattern_test_data['8bit'] - self.bist_checker_test(memory=data['expected'], data_width=8, pattern=data['pattern']) + 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.bist_checker_test(memory=data['expected'], data_width=32, pattern=data['pattern']) + 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.bist_checker_test(memory=data['expected'], data_width=64, pattern=data['pattern']) + 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.bist_checker_test(memory=data['expected'], data_width=32, pattern=data['pattern']) + 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'])) - self.bist_checker_test(memory=data['expected'], data_width=32, pattern=data['pattern'], - expected_errors=num_duplicates) + dut, checker = self.checker_test(memory=data['expected'], data_width=32, pattern=data['pattern'], check_errors=False) + self.assertEqual(checker.errors, num_duplicates) def test_bist(self): class DUT(Module): From ef9b13d7e85f5e321e2ab00c75ea12293a097829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Mon, 16 Mar 2020 16:02:24 +0100 Subject: [PATCH 06/12] test: add tests for BIST modules with clock domain crossing --- test/test_bist.py | 154 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 124 insertions(+), 30 deletions(-) diff --git a/test/test_bist.py b/test/test_bist.py index f9e59ae..1fe7b95 100644 --- a/test/test_bist.py +++ b/test/test_bist.py @@ -53,6 +53,38 @@ class GenCheckDriver: self.errors = (yield self.module.errors) +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(unittest.TestCase): def setUp(self): # define common test data used for both generator and checker tests @@ -448,7 +480,39 @@ class TestBIST(unittest.TestCase): dut, checker = self.checker_test(memory=data['expected'], data_width=32, pattern=data['pattern'], check_errors=False) self.assertEqual(checker.errors, num_duplicates) - def test_bist(self): + 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) @@ -459,38 +523,11 @@ 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.configure(16, 64) - yield from generator.run() - - # read (no errors) - yield from checker.reset() - yield from checker.configure(16, 64) - yield from checker.run() - assert checker.errors == 0 - - # corrupt memory (using generator) - yield from generator.reset() - yield from generator.configure(16 + 60, 64) - yield from generator.run() - - # read (4 errors) - yield from checker.reset() - yield from checker.configure(16, 64) - yield from checker.run() - assert checker.errors != 0 - - # read (no errors) - yield from checker.reset() - yield from checker.configure(16 + 60, 64) - yield from checker.run() - 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 = [ @@ -499,3 +536,60 @@ class TestBIST(unittest.TestCase): 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) + + def test_bist_csr_cdc(self): + class DUT(Module): + def __init__(self): + self.write_port = LiteDRAMNativeWritePort(address_width=32, data_width=32, clock_domain='async') + self.read_port = LiteDRAMNativeReadPort(address_width=32, data_width=32, clock_domain='async') + 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) From 5618d2a54c5a2279b8a8e28b6dd801358f0ca69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Tue, 17 Mar 2020 09:45:28 +0100 Subject: [PATCH 07/12] test: fix quotes --- test/test_bist.py | 154 +++++++++++++++++++++++----------------------- 1 file changed, 76 insertions(+), 78 deletions(-) diff --git a/test/test_bist.py b/test/test_bist.py index 1fe7b95..5bd533b 100644 --- a/test/test_bist.py +++ b/test/test_bist.py @@ -3,7 +3,6 @@ # License: BSD import unittest -import random from migen import * @@ -89,14 +88,14 @@ class TestBIST(unittest.TestCase): def setUp(self): # define common test data used for both generator and checker tests self.bist_test_data = { - '8bit': dict( + "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( + "32bit": dict( base = 0x04, end = 0x04 + 8, length = 5 * 4, @@ -111,7 +110,7 @@ class TestBIST(unittest.TestCase): 0x00000000, # 0x1c ], ), - '64bit': dict( + "64bit": dict( base = 0x10, end = 0x10 + 8, length = 5 * 8, @@ -126,7 +125,7 @@ class TestBIST(unittest.TestCase): 0x0000000000000000, # 0x38 ], ), - '32bit_masked': dict( + "32bit_masked": dict( base = 0x04, end = 0x04 + 0x04, # TODO: fix address masking to be consistent length = 6 * 4, @@ -142,17 +141,17 @@ class TestBIST(unittest.TestCase): ], ), } - self.bist_test_data['32bit_long_sequential'] = dict( + self.bist_test_data["32bit_long_sequential"] = dict( base = 16, end = 16 + 128, length = 64, expected = [0x00000000] * 128 ) - expected = self.bist_test_data['32bit_long_sequential']['expected'] + expected = self.bist_test_data["32bit_long_sequential"]["expected"] expected[16//4:(16 + 64)//4] = list(range(64//4)) self.pattern_test_data = { - '8bit': dict( + "8bit": dict( pattern = [ # address, data (0x00, 0xaa), @@ -172,7 +171,7 @@ class TestBIST(unittest.TestCase): 0xdd, # 0x07 ], ), - '32bit': dict( + "32bit": dict( pattern = [ # address, data (0x00, 0xabadcafe), @@ -192,7 +191,7 @@ class TestBIST(unittest.TestCase): 0xbaadf00d, # 0x1c ], ), - '64bit': dict( + "64bit": dict( pattern = [ # address, data (0x00, 0x0ddf00dbadc0ffee), @@ -212,7 +211,7 @@ class TestBIST(unittest.TestCase): 0xdeadc0debaadbeef, # 0x38 ], ), - '32bit_not_aligned': dict( + "32bit_not_aligned": dict( pattern = [ # address, data (0x00, 0xabadcafe), @@ -232,7 +231,7 @@ class TestBIST(unittest.TestCase): 0xbaadf00d, # 0x1c ], ), - '32bit_duplicates': dict( + "32bit_duplicates": dict( pattern = [ # address, data (0x00, 0xabadcafe), @@ -252,7 +251,7 @@ class TestBIST(unittest.TestCase): 0xdeadc0de, # 0x1c ], ), - '32bit_sequential': dict( + "32bit_sequential": dict( pattern = [ # address, data (0x02, 0xabadcafe), @@ -275,8 +274,6 @@ class TestBIST(unittest.TestCase): } def test_generator(self): - port = LiteDRAMNativeWritePort(address_width=32, data_width=32) - def main_generator(dut): self.errors = 0 @@ -304,13 +301,13 @@ 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 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' + assert pattern is None or config_args is None, "_LiteDRAMBISTGenerator xor _LiteDRAMPatternGenerator" class DUT(Module): def __init__(self): @@ -339,8 +336,8 @@ class TestBIST(unittest.TestCase): return dut def test_bist_generator_8bit(self): - data = self.bist_test_data['8bit'] - self.generator_test(data.pop('expected'), data_width=8, config_args=data) + 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: @@ -348,73 +345,73 @@ class TestBIST(unittest.TestCase): # 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') + 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) + 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) + 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) + 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) + 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): - 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) + 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') + 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): # write whole memory and check if there are no repetitions? - 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) + 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 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') + 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_pattern_generator_8bit(self): - data = self.pattern_test_data['8bit'] - self.generator_test(data['expected'], data_width=8, pattern=data['pattern']) + 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']) + 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']) + 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']) + 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']) + 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']) + data = self.pattern_test_data["32bit_sequential"] + self.generator_test(data["expected"], data_width=32, pattern=data["pattern"]) 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' + assert pattern is None or config_args is None, "_LiteDRAMBISTChecker xor _LiteDRAMPatternChecker" class DUT(Module): def __init__(self): @@ -444,40 +441,41 @@ class TestBIST(unittest.TestCase): return dut, checker def test_bist_checker_8bit(self): - data = self.bist_test_data['8bit'] - memory = data.pop('expected') + data = self.bist_test_data["8bit"] + memory = data.pop("expected") self.checker_test(memory, data_width=8, config_args=data) def test_bist_checker_32bit(self): - data = self.bist_test_data['32bit'] - memory = data.pop('expected') + 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') + data = self.bist_test_data["32bit"] + memory = data.pop("expected") self.checker_test(memory, data_width=32, config_args=data) def test_pattern_checker_8bit(self): - data = self.pattern_test_data['8bit'] - self.checker_test(memory=data['expected'], data_width=8, pattern=data['pattern']) + 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']) + 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']) + 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']) + 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) + 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) def bist_test(self, generator, checker, mem): @@ -534,7 +532,7 @@ class TestBIST(unittest.TestCase): 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): @@ -559,14 +557,14 @@ class TestBIST(unittest.TestCase): main_generator(dut, mem), mem.write_handler(dut.write_port), mem.read_handler(dut.read_port) - ] + ] run_simulation(dut, generators) def test_bist_csr_cdc(self): class DUT(Module): def __init__(self): - self.write_port = LiteDRAMNativeWritePort(address_width=32, data_width=32, clock_domain='async') - self.read_port = LiteDRAMNativeReadPort(address_width=32, data_width=32, clock_domain='async') + self.write_port = LiteDRAMNativeWritePort(address_width=32, data_width=32, clock_domain="async") + self.read_port = LiteDRAMNativeReadPort(address_width=32, data_width=32, clock_domain="async") self.submodules.generator = LiteDRAMBISTGenerator(self.write_port) self.submodules.checker = LiteDRAMBISTChecker(self.read_port) @@ -580,16 +578,16 @@ class TestBIST(unittest.TestCase): mem = DRAMMemory(32, 48) generators = { - 'sys': [ + "sys": [ main_generator(dut, mem), ], - 'async': [ + "async": [ mem.write_handler(dut.write_port), mem.read_handler(dut.read_port) ] } clocks = { - 'sys': 10, - 'async': (7, 3), + "sys": 10, + "async": (7, 3), } run_simulation(dut, generators, clocks) From d86ebd7e9d0cddedf67a5f032733bd16aff7f9c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Tue, 17 Mar 2020 12:39:10 +0100 Subject: [PATCH 08/12] test: add LiteDRAMDMAWriter tests --- test/test_bist.py | 79 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/test/test_bist.py b/test/test_bist.py index 5bd533b..958a487 100644 --- a/test/test_bist.py +++ b/test/test_bist.py @@ -84,6 +84,30 @@ class GenCheckCSRDriver: self.errors = (yield from self.module.errors.read()) +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 + while (yield self.dma.sink.ready): + 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 + while (yield port.wdata.ready): + yield + + class TestBIST(unittest.TestCase): def setUp(self): # define common test data used for both generator and checker tests @@ -271,7 +295,12 @@ class TestBIST(unittest.TestCase): 0x00000000, # 0x1c ], ), + "32bit_long_sequential": dict(pattern=[], expected=[0] * 64), } + for i in range(32): + data = self.pattern_test_data["32bit_long_sequential"] + data['pattern'].append((i, 64 + i)) + data['expected'][i] = 64 + i def test_generator(self): def main_generator(dut): @@ -591,3 +620,53 @@ class TestBIST(unittest.TestCase): "async": (7, 3), } run_simulation(dut, generators, clocks) + + def dma_writer_test_pattern(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(pattern, mem_expected, data_width=32) + + def test_dma_writer_multiple(self): + data = self.pattern_test_data["32bit"] + self.dma_writer_test_pattern(data["pattern"], data["expected"], data_width=32) + + def test_dma_writer_sequential(self): + data = self.pattern_test_data["32bit_sequential"] + self.dma_writer_test_pattern(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_pattern(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_pattern(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_pattern(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_pattern(data["pattern"], data["expected"], data_width=32) From a883f88cca31057b19032f3c7d584915790b19c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Tue, 17 Mar 2020 13:08:34 +0100 Subject: [PATCH 09/12] test: add LiteDRAMDMAReader tests --- test/test_bist.py | 101 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 13 deletions(-) diff --git a/test/test_bist.py b/test/test_bist.py index 958a487..afdcdbe 100644 --- a/test/test_bist.py +++ b/test/test_bist.py @@ -108,6 +108,35 @@ class DMAWriterDriver: 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: + while not (yield self.dma.source.valid): + yield + data = (yield self.dma.source.data) + self.data.append(data) + yield + + class TestBIST(unittest.TestCase): def setUp(self): # define common test data used for both generator and checker tests @@ -299,8 +328,8 @@ class TestBIST(unittest.TestCase): } for i in range(32): data = self.pattern_test_data["32bit_long_sequential"] - data['pattern'].append((i, 64 + i)) - data['expected'][i] = 64 + i + data["pattern"].append((i, 64 + i)) + data["expected"][i] = 64 + i def test_generator(self): def main_generator(dut): @@ -405,7 +434,7 @@ class TestBIST(unittest.TestCase): 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): # write whole memory and check if there are no repetitions? + 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) @@ -621,7 +650,7 @@ class TestBIST(unittest.TestCase): } run_simulation(dut, generators, clocks) - def dma_writer_test_pattern(self, pattern, mem_expected, data_width, **kwargs): + 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) @@ -643,30 +672,76 @@ class TestBIST(unittest.TestCase): pattern = [(0x04, 0xdeadc0de)] mem_expected = [0] * 32 mem_expected[0x04] = 0xdeadc0de - self.dma_writer_test_pattern(pattern, mem_expected, data_width=32) + 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_pattern(data["pattern"], data["expected"], data_width=32) + 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_pattern(data["pattern"], data["expected"], data_width=32) + 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_pattern(data["pattern"], data["expected"], data_width=32) + 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_pattern(data["pattern"], data["expected"], data_width=32, - fifo_depth=1) + 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_pattern(data["pattern"], data["expected"], data_width=32, - fifo_buffered=True) + 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_pattern(data["pattern"], data["expected"], data_width=32) + self.dma_writer_test(data["pattern"], data["expected"], data_width=32) + + 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) From 6ef623efae5755ab5879e5beb9c38843d0a2d73f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Tue, 17 Mar 2020 14:23:08 +0100 Subject: [PATCH 10/12] test: cleanup test_bist.py code style --- test/test_bist.py | 150 ++++++++++++++++++++++++++-------------------- 1 file changed, 86 insertions(+), 64 deletions(-) diff --git a/test/test_bist.py b/test/test_bist.py index afdcdbe..890e863 100644 --- a/test/test_bist.py +++ b/test/test_bist.py @@ -142,77 +142,77 @@ class TestBIST(unittest.TestCase): # define common test data used for both generator and checker tests self.bist_test_data = { "8bit": dict( - base = 2, - end = 2 + 8, # (end - base) must be pow of 2 - length = 5, + 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], + 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 + 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 + 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 + 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 ], ), } self.bist_test_data["32bit_long_sequential"] = dict( - base = 16, - end = 16 + 128, - length = 64, - expected = [0x00000000] * 128 + base=16, + end=16 + 128, + length=64, + expected=[0x00000000] * 128 ) expected = self.bist_test_data["32bit_long_sequential"]["expected"] expected[16//4:(16 + 64)//4] = list(range(64//4)) self.pattern_test_data = { "8bit": dict( - pattern = [ + pattern=[ # address, data (0x00, 0xaa), (0x05, 0xbb), (0x02, 0xcc), (0x07, 0xdd), ], - expected = [ + expected=[ # data, address 0xaa, # 0x00 0x00, # 0x01 @@ -225,14 +225,14 @@ class TestBIST(unittest.TestCase): ], ), "32bit": dict( - pattern = [ + pattern=[ # address, data (0x00, 0xabadcafe), (0x07, 0xbaadf00d), (0x02, 0xcafefeed), (0x01, 0xdeadc0de), ], - expected = [ + expected=[ # data, address 0xabadcafe, # 0x00 0xdeadc0de, # 0x04 @@ -245,14 +245,14 @@ class TestBIST(unittest.TestCase): ], ), "64bit": dict( - pattern = [ + pattern=[ # address, data (0x00, 0x0ddf00dbadc0ffee), (0x05, 0xabadcafebaadf00d), (0x02, 0xcafefeedfeedface), (0x07, 0xdeadc0debaadbeef), ], - expected = [ + expected=[ # data, address 0x0ddf00dbadc0ffee, # 0x00 0x0000000000000000, # 0x08 @@ -265,14 +265,14 @@ class TestBIST(unittest.TestCase): ], ), "32bit_not_aligned": dict( - pattern = [ + pattern=[ # address, data (0x00, 0xabadcafe), (0x07, 0xbaadf00d), (0x02, 0xcafefeed), (0x01, 0xdeadc0de), ], - expected = [ + expected=[ # data, address 0xabadcafe, # 0x00 0xdeadc0de, # 0x04 @@ -285,14 +285,14 @@ class TestBIST(unittest.TestCase): ], ), "32bit_duplicates": dict( - pattern = [ + pattern=[ # address, data (0x00, 0xabadcafe), (0x07, 0xbaadf00d), (0x00, 0xcafefeed), (0x07, 0xdeadc0de), ], - expected = [ + expected=[ # data, address 0xcafefeed, # 0x00 0x00000000, # 0x04 @@ -305,14 +305,14 @@ class TestBIST(unittest.TestCase): ], ), "32bit_sequential": dict( - pattern = [ + pattern=[ # address, data (0x02, 0xabadcafe), (0x03, 0xbaadf00d), (0x04, 0xcafefeed), (0x05, 0xdeadc0de), ], - expected = [ + expected=[ # data, address 0x00000000, # 0x00 0x00000000, # 0x04 @@ -331,6 +331,8 @@ class TestBIST(unittest.TestCase): data["pattern"].append((i, 64 + i)) data["expected"][i] = 64 + i + # Generator ------------------------------------------------------------------------------------ + def test_generator(self): def main_generator(dut): self.errors = 0 @@ -364,14 +366,16 @@ class TestBIST(unittest.TestCase): run_simulation(dut, generators) self.assertEqual(self.errors, 0) - 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" + 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) if pattern is not None: - self.submodules.generator = _LiteDRAMPatternGenerator(self.write_port, init=pattern) + self.submodules.generator = _LiteDRAMPatternGenerator(self.write_port, pattern) else: self.submodules.generator = _LiteDRAMBISTGenerator(self.write_port) self.mem = DRAMMemory(data_width, len(mem_expected)) @@ -393,6 +397,8 @@ class TestBIST(unittest.TestCase): self.assertEqual(dut.mem.mem, mem_expected) return dut + # _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) @@ -428,7 +434,8 @@ class TestBIST(unittest.TestCase): def test_bist_generator_random_data(self): 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) + 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") @@ -437,13 +444,16 @@ class TestBIST(unittest.TestCase): 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) + 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 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") + # _LiteDRAMPatternGenerator -------------------------------------------------------------------- + def test_pattern_generator_8bit(self): data = self.pattern_test_data["8bit"] self.generator_test(data["expected"], data_width=8, pattern=data["pattern"]) @@ -468,8 +478,11 @@ class TestBIST(unittest.TestCase): 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" + assert pattern is None or config_args is None, \ + "_LiteDRAMBISTChecker xor _LiteDRAMPatternChecker" class DUT(Module): def __init__(self): @@ -513,6 +526,8 @@ class TestBIST(unittest.TestCase): 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"]) @@ -536,6 +551,8 @@ class TestBIST(unittest.TestCase): 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() @@ -621,8 +638,9 @@ class TestBIST(unittest.TestCase): def test_bist_csr_cdc(self): class DUT(Module): def __init__(self): - self.write_port = LiteDRAMNativeWritePort(address_width=32, data_width=32, clock_domain="async") - self.read_port = LiteDRAMNativeReadPort(address_width=32, data_width=32, clock_domain="async") + 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) @@ -650,6 +668,8 @@ class TestBIST(unittest.TestCase): } run_simulation(dut, generators, clocks) + # LiteDRAMDMAWriter ---------------------------------------------------------------------------- + def dma_writer_test(self, pattern, mem_expected, data_width, **kwargs): class DUT(Module): def __init__(self): @@ -700,6 +720,8 @@ class TestBIST(unittest.TestCase): 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): From 36d5b42aa03edb10a3580426bce69be983a39b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Tue, 17 Mar 2020 15:37:50 +0100 Subject: [PATCH 11/12] test: correct DMAReaderDriver/DMAWriterDriver logic --- test/test_bist.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/test/test_bist.py b/test/test_bist.py index 890e863..29980e0 100644 --- a/test/test_bist.py +++ b/test/test_bist.py @@ -95,8 +95,7 @@ class DMAWriterDriver: yield self.dma.sink.data.eq(data) while not (yield self.dma.sink.ready): yield - while (yield self.dma.sink.ready): - yield + yield yield self.dma.sink.valid.eq(0) @staticmethod @@ -104,8 +103,7 @@ class DMAWriterDriver: for _ in range(n): while not (yield port.wdata.ready): yield - while (yield port.wdata.ready): - yield + yield class DMAReaderDriver: @@ -130,10 +128,8 @@ class DMAReaderDriver: def read_handler(self): yield self.dma.source.ready.eq(1) while True: - while not (yield self.dma.source.valid): - yield - data = (yield self.dma.source.data) - self.data.append(data) + if (yield self.dma.source.valid): + self.data.append((yield self.dma.source.data)) yield From 03f93998b5e5bf2120cda670e31df03811442551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Thu, 19 Mar 2020 09:13:28 +0100 Subject: [PATCH 12/12] test: move DMA specific tests to test_dma.py --- test/common.py | 198 ++++++++++++++++++++++ test/test_bist.py | 413 ++++------------------------------------------ test/test_dma.py | 165 ++++++++++++++++++ 3 files changed, 399 insertions(+), 377 deletions(-) create mode 100644 test/test_dma.py 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 29980e0..b83dc0f 100644 --- a/test/test_bist.py +++ b/test/test_bist.py @@ -1,12 +1,13 @@ # 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 from migen import * -from litex.soc.interconnect.stream import * +from litex.gen.sim import * from litedram.common import * from litedram.frontend.bist import * @@ -15,8 +16,6 @@ from litedram.frontend.bist import _LiteDRAMBISTGenerator, _LiteDRAMBISTChecker, from test.common import * -from litex.gen.sim import * - class GenCheckDriver: def __init__(self, module): @@ -84,248 +83,7 @@ class GenCheckCSRDriver: self.errors = (yield from self.module.errors.read()) -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 TestBIST(unittest.TestCase): - def setUp(self): - # define common test data used for both generator and checker tests - self.bist_test_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 - ], - ), - } - self.bist_test_data["32bit_long_sequential"] = dict( - base=16, - end=16 + 128, - length=64, - expected=[0x00000000] * 128 - ) - expected = self.bist_test_data["32bit_long_sequential"]["expected"] - expected[16//4:(16 + 64)//4] = list(range(64//4)) - - self.pattern_test_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 = self.pattern_test_data["32bit_long_sequential"] - data["pattern"].append((i, 64 + i)) - data["expected"][i] = 64 + i +class TestBIST(MemoryTestDataMixin, unittest.TestCase): # Generator ------------------------------------------------------------------------------------ @@ -631,135 +389,36 @@ class TestBIST(unittest.TestCase): ] run_simulation(dut, generators) - 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) - - # 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) + # 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)