From e901305e56096398e109fd09da62fa04dc3a268a Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Fri, 16 Dec 2016 16:43:20 +0100 Subject: [PATCH 01/12] bist: Done only goes high after bist runs. --- litedram/frontend/bist.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/litedram/frontend/bist.py b/litedram/frontend/bist.py index a60c5fa..fe434c3 100644 --- a/litedram/frontend/bist.py +++ b/litedram/frontend/bist.py @@ -56,7 +56,7 @@ class _LiteDRAMBISTGenerator(Module): else: self.submodules.gen = gen = Counter(dram_port.dw) - shooted = Signal() + self.shooted = shooted = Signal() enable = Signal() counter = Signal(dram_port.aw) self.comb += enable.eq(shooted & (counter != (self.length - 1))) @@ -75,7 +75,7 @@ class _LiteDRAMBISTGenerator(Module): dma.sink.data.eq(gen.o), gen.ce.eq(enable & dma.sink.ready), - self.done.eq(~enable) + self.done.eq(~enable & shooted) ] @@ -138,7 +138,7 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): else: self.submodules.gen = gen = Counter(dram_port.dw) - shooted = Signal() + self.shooted = shooted = Signal() address_counter = Signal(dram_port.aw) address_counter_ce = Signal() data_counter = Signal(dram_port.aw) @@ -183,7 +183,7 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): ) self.comb += data_counter_ce.eq(dma.source.valid) - self.comb += self.done.eq(~data_enable & ~address_enable) + self.comb += self.done.eq(~data_enable & ~address_enable & shooted) class LiteDRAMBISTChecker(Module, AutoCSR): From 85e4b655502183a37e544dfb218e8fe23350c84c Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Fri, 16 Dec 2016 16:43:51 +0100 Subject: [PATCH 02/12] bist: Small formatting change. --- litedram/frontend/bist.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/litedram/frontend/bist.py b/litedram/frontend/bist.py index fe434c3..e10410f 100644 --- a/litedram/frontend/bist.py +++ b/litedram/frontend/bist.py @@ -175,12 +175,13 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): gen.ce.eq(dma.source.valid), dma.source.ready.eq(1) ] - self.sync += \ + self.sync += [ If(dma.source.valid, If(dma.source.data != gen.o, self.error_count.eq(self.error_count + 1) ) - ) + ), + ] self.comb += data_counter_ce.eq(dma.source.valid) self.comb += self.done.eq(~data_enable & ~address_enable & shooted) From 6ae11fa5c8084de698d71bfec416dfdfea44219c Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Fri, 16 Dec 2016 16:45:23 +0100 Subject: [PATCH 03/12] bist: Make reset write to activate like shoot. --- litedram/frontend/bist.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/litedram/frontend/bist.py b/litedram/frontend/bist.py index e10410f..bb7b9b6 100644 --- a/litedram/frontend/bist.py +++ b/litedram/frontend/bist.py @@ -81,7 +81,7 @@ class _LiteDRAMBISTGenerator(Module): class LiteDRAMBISTGenerator(Module, AutoCSR): def __init__(self, dram_port, random=True): - self.reset = CSRStorage() + self.reset = CSR() self.shoot = CSR() self.done = CSRStatus() self.base = CSRStorage(dram_port.aw) @@ -104,7 +104,7 @@ class LiteDRAMBISTGenerator(Module, AutoCSR): self.submodules += base_sync, length_sync self.comb += [ - reset_sync.i.eq(self.reset.storage), + reset_sync.i.eq(self.reset.re), core.reset.eq(reset_sync.o), shoot_sync.i.eq(self.shoot.re), @@ -189,7 +189,7 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): class LiteDRAMBISTChecker(Module, AutoCSR): def __init__(self, dram_port, random=True): - self.reset = CSRStorage() + self.reset = CSR() self.shoot = CSR() self.done = CSRStatus() self.base = CSRStorage(dram_port.aw) From 086b905e59edd7cfb15f3b5eeb2c1fb4ca96fbf4 Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Fri, 16 Dec 2016 16:46:03 +0100 Subject: [PATCH 04/12] bist: Improve the basic test bench a little. --- test/bist_tb.py | 88 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 10 deletions(-) diff --git a/test/bist_tb.py b/test/bist_tb.py index 6580b4c..acad27a 100755 --- a/test/bist_tb.py +++ b/test/bist_tb.py @@ -1,5 +1,7 @@ #!/usr/bin/env python3 +import random + from litex.gen import * from litex.soc.interconnect.stream import * @@ -17,15 +19,42 @@ class TB(Module): self.submodules.generator = LiteDRAMBISTGenerator(self.write_port) self.submodules.checker = LiteDRAMBISTChecker(self.read_port) -def main_generator(dut): - # init - yield dut.generator.reset.storage.eq(1) - yield dut.checker.reset.storage.eq(1) + +def togglereset(module): + resig = module.reset.re + + # Check that reset isn't set + reval = yield resig + assert not reval, reval + + # Toggle the reset + yield resig.eq(1) yield - yield dut.generator.reset.storage.eq(0) - yield dut.checker.reset.storage.eq(0) + yield resig.eq(0) + yield # Takes 3 clock cycles for the reset to have an effect yield + yield + yield + yield + yield + + # Check some initial conditions are correct after reset. + shooted = yield module.core.shooted + assert shooted == 0, shooted + + done = yield module.done.status + assert not done, done + + +def main_generator(dut, mem): + # Populate memory with random data + random.seed(0) + for i in range(0, len(mem.mem)): + mem.mem[i] = random.randint(0, 2**mem.width) + # write + yield from togglereset(dut.generator) + yield dut.generator.base.storage.eq(16) yield dut.generator.length.storage.eq(64) for i in range(8): @@ -37,7 +66,14 @@ def main_generator(dut): yield while((yield dut.generator.done.status) == 0): yield - # read + done = yield dut.generator.done.status + assert done, done + + # read with no errors + yield from togglereset(dut.checker) + errors = yield dut.checker.error_count.status + assert errors == 0, errors + yield dut.checker.base.storage.eq(16) yield dut.checker.length.storage.eq(64) for i in range(8): @@ -49,15 +85,47 @@ def main_generator(dut): yield while((yield dut.checker.done.status) == 0): yield - # check - print("errors {:d}".format((yield dut.checker.error_count.status))) + done = yield dut.checker.done.status + assert done, done + errors = yield dut.checker.error_count.status + assert errors == 0, errors + yield + yield + + # read with one error + yield from togglereset(dut.checker) + errors = yield dut.checker.error_count.status + assert errors == 0, errors + + assert mem.mem[20] != 0, mem.mem[20] + mem.mem[20] = 0 # Make position 20 an error + + yield dut.checker.base.storage.eq(16) + yield dut.checker.length.storage.eq(64) + for i in range(8): + yield + yield dut.checker.shoot.re.eq(1) + yield + yield dut.checker.shoot.re.eq(0) + for i in range(8): + yield + while((yield dut.checker.done.status) == 0): + yield + done = yield dut.checker.done.status + assert done, done + errors = yield dut.checker.error_count.status + assert errors == 1, errors + + yield + yield + if __name__ == "__main__": tb = TB() mem = DRAMMemory(32, 128) generators = { - "sys" : [main_generator(tb), + "sys" : [main_generator(tb, mem), mem.write_generator(tb.write_port), mem.read_generator(tb.read_port)] } From dc14a98bf40263b6c1ac6ec2bf958d6b50256f50 Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Fri, 16 Dec 2016 16:48:28 +0100 Subject: [PATCH 05/12] bist: s/shoot/start/ --- litedram/frontend/bist.py | 50 +++++++++++++++++++-------------------- test/bist_async_tb.py | 8 +++---- test/bist_tb.py | 16 ++++++------- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/litedram/frontend/bist.py b/litedram/frontend/bist.py index bb7b9b6..e595da7 100644 --- a/litedram/frontend/bist.py +++ b/litedram/frontend/bist.py @@ -42,7 +42,7 @@ class Counter(Module): class _LiteDRAMBISTGenerator(Module): def __init__(self, dram_port, random): - self.shoot = Signal() + self.start = Signal() self.done = Signal() self.base = Signal(dram_port.aw) self.length = Signal(dram_port.aw) @@ -56,13 +56,13 @@ class _LiteDRAMBISTGenerator(Module): else: self.submodules.gen = gen = Counter(dram_port.dw) - self.shooted = shooted = Signal() + self.started = started = Signal() enable = Signal() counter = Signal(dram_port.aw) - self.comb += enable.eq(shooted & (counter != (self.length - 1))) + self.comb += enable.eq(started & (counter != (self.length - 1))) self.sync += [ - If(self.shoot, - shooted.eq(1), + If(self.start, + started.eq(1), counter.eq(0) ).Elif(gen.ce, counter.eq(counter + 1) @@ -75,14 +75,14 @@ class _LiteDRAMBISTGenerator(Module): dma.sink.data.eq(gen.o), gen.ce.eq(enable & dma.sink.ready), - self.done.eq(~enable & shooted) + self.done.eq(~enable & started) ] class LiteDRAMBISTGenerator(Module, AutoCSR): def __init__(self, dram_port, random=True): self.reset = CSR() - self.shoot = CSR() + self.start = CSR() self.done = CSRStatus() self.base = CSRStorage(dram_port.aw) self.length = CSRStorage(dram_port.aw) @@ -95,9 +95,9 @@ class LiteDRAMBISTGenerator(Module, AutoCSR): self.submodules.core = ClockDomainsRenamer(cd)(core) reset_sync = BusSynchronizer(1, "sys", cd) - shoot_sync = PulseSynchronizer("sys", cd) + start_sync = PulseSynchronizer("sys", cd) done_sync = BusSynchronizer(1, cd, "sys") - self.submodules += reset_sync, shoot_sync, done_sync + self.submodules += reset_sync, start_sync, done_sync base_sync = BusSynchronizer(dram_port.aw, "sys", cd) length_sync = BusSynchronizer(dram_port.aw, "sys", cd) @@ -107,8 +107,8 @@ class LiteDRAMBISTGenerator(Module, AutoCSR): reset_sync.i.eq(self.reset.re), core.reset.eq(reset_sync.o), - shoot_sync.i.eq(self.shoot.re), - core.shoot.eq(shoot_sync.o), + start_sync.i.eq(self.start.re), + core.start.eq(start_sync.o), done_sync.i.eq(core.done), self.done.status.eq(done_sync.o), @@ -123,7 +123,7 @@ class LiteDRAMBISTGenerator(Module, AutoCSR): class _LiteDRAMBISTChecker(Module, AutoCSR): def __init__(self, dram_port, random): - self.shoot = Signal() + self.start = Signal() self.done = Signal() self.base = Signal(dram_port.aw) self.length = Signal(dram_port.aw) @@ -138,21 +138,21 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): else: self.submodules.gen = gen = Counter(dram_port.dw) - self.shooted = shooted = Signal() + self.started = started = Signal() address_counter = Signal(dram_port.aw) address_counter_ce = Signal() data_counter = Signal(dram_port.aw) data_counter_ce = Signal() self.sync += [ - If(self.shoot, - shooted.eq(1) + If(self.start, + started.eq(1) ), - If(self.shoot, + If(self.start, address_counter.eq(0) ).Elif(address_counter_ce, address_counter.eq(address_counter + 1) ), - If(self.shoot, + If(self.start, data_counter.eq(0), ).Elif(data_counter_ce, data_counter.eq(data_counter + 1) @@ -160,7 +160,7 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): ] address_enable = Signal() - self.comb += address_enable.eq(shooted & (address_counter != (self.length - 1))) + self.comb += address_enable.eq(started & (address_counter != (self.length - 1))) self.comb += [ dma.sink.valid.eq(address_enable), @@ -169,7 +169,7 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): ] data_enable = Signal() - self.comb += data_enable.eq(shooted & (data_counter != (self.length - 1))) + self.comb += data_enable.eq(started & (data_counter != (self.length - 1))) self.comb += [ gen.ce.eq(dma.source.valid), @@ -184,13 +184,13 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): ] self.comb += data_counter_ce.eq(dma.source.valid) - self.comb += self.done.eq(~data_enable & ~address_enable & shooted) + self.comb += self.done.eq(~data_enable & ~address_enable & started) class LiteDRAMBISTChecker(Module, AutoCSR): def __init__(self, dram_port, random=True): self.reset = CSR() - self.shoot = CSR() + self.start = CSR() self.done = CSRStatus() self.base = CSRStorage(dram_port.aw) self.length = CSRStorage(dram_port.aw) @@ -204,9 +204,9 @@ class LiteDRAMBISTChecker(Module, AutoCSR): self.submodules.core = ClockDomainsRenamer(cd)(core) reset_sync = BusSynchronizer(1, "sys", cd) - shoot_sync = PulseSynchronizer("sys", cd) + start_sync = PulseSynchronizer("sys", cd) done_sync = BusSynchronizer(1, cd, "sys") - self.submodules += reset_sync, shoot_sync, done_sync + self.submodules += reset_sync, start_sync, done_sync base_sync = BusSynchronizer(dram_port.aw, "sys", cd) length_sync = BusSynchronizer(dram_port.aw, "sys", cd) @@ -217,8 +217,8 @@ class LiteDRAMBISTChecker(Module, AutoCSR): reset_sync.i.eq(self.reset.re), core.reset.eq(reset_sync.o), - shoot_sync.i.eq(self.shoot.re), - core.shoot.eq(shoot_sync.o), + start_sync.i.eq(self.start.re), + core.start.eq(start_sync.o), done_sync.i.eq(core.done), self.done.status.eq(done_sync.o), diff --git a/test/bist_async_tb.py b/test/bist_async_tb.py index f721ed6..699a6ba 100755 --- a/test/bist_async_tb.py +++ b/test/bist_async_tb.py @@ -81,9 +81,9 @@ def main_generator(dut): yield dut.generator.length.storage.eq(16) for i in range(32): yield - yield dut.generator.shoot.re.eq(1) + yield dut.generator.start.re.eq(1) yield - yield dut.generator.shoot.re.eq(0) + yield dut.generator.start.re.eq(0) for i in range(32): yield while((yield dut.generator.done.status) == 0): @@ -93,9 +93,9 @@ def main_generator(dut): yield dut.checker.length.storage.eq(16) for i in range(32): yield - yield dut.checker.shoot.re.eq(1) + yield dut.checker.start.re.eq(1) yield - yield dut.checker.shoot.re.eq(0) + yield dut.checker.start.re.eq(0) for i in range(32): yield while((yield dut.checker.done.status) == 0): diff --git a/test/bist_tb.py b/test/bist_tb.py index acad27a..f4fe618 100755 --- a/test/bist_tb.py +++ b/test/bist_tb.py @@ -39,8 +39,8 @@ def togglereset(module): yield # Check some initial conditions are correct after reset. - shooted = yield module.core.shooted - assert shooted == 0, shooted + started = yield module.core.started + assert started == 0, started done = yield module.done.status assert not done, done @@ -59,9 +59,9 @@ def main_generator(dut, mem): yield dut.generator.length.storage.eq(64) for i in range(8): yield - yield dut.generator.shoot.re.eq(1) + yield dut.generator.start.re.eq(1) yield - yield dut.generator.shoot.re.eq(0) + yield dut.generator.start.re.eq(0) for i in range(8): yield while((yield dut.generator.done.status) == 0): @@ -78,9 +78,9 @@ def main_generator(dut, mem): yield dut.checker.length.storage.eq(64) for i in range(8): yield - yield dut.checker.shoot.re.eq(1) + yield dut.checker.start.re.eq(1) yield - yield dut.checker.shoot.re.eq(0) + yield dut.checker.start.re.eq(0) for i in range(8): yield while((yield dut.checker.done.status) == 0): @@ -105,9 +105,9 @@ def main_generator(dut, mem): yield dut.checker.length.storage.eq(64) for i in range(8): yield - yield dut.checker.shoot.re.eq(1) + yield dut.checker.start.re.eq(1) yield - yield dut.checker.shoot.re.eq(0) + yield dut.checker.start.re.eq(0) for i in range(8): yield while((yield dut.checker.done.status) == 0): From da144f41d4efa8b344ee49c7f5ff4a2da1fcce17 Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Fri, 16 Dec 2016 16:58:01 +0100 Subject: [PATCH 06/12] bist: Refactoring test bench. Move a bunch of common code into common.py --- test/bist_async_tb.py | 20 +++++++------------- test/bist_tb.py | 42 ++++++------------------------------------ test/common.py | 27 +++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 49 deletions(-) diff --git a/test/bist_async_tb.py b/test/bist_async_tb.py index 699a6ba..3930f50 100755 --- a/test/bist_async_tb.py +++ b/test/bist_async_tb.py @@ -12,9 +12,11 @@ from litedram.frontend.bist import LiteDRAMBISTGenerator from litedram.frontend.bist import LiteDRAMBISTChecker from litedram.frontend.adaptation import LiteDRAMPortCDC - from litedram.phy.model import SDRAMPHYModel +from test.common import * + + class SimModule(SDRAMModule): # geometry nbanks = 2 @@ -70,20 +72,14 @@ def main_generator(dut): for i in range(100): yield # init - yield dut.generator.reset.storage.eq(1) - yield dut.checker.reset.storage.eq(1) - yield - yield dut.generator.reset.storage.eq(0) - yield dut.checker.reset.storage.eq(0) - yield + yield from reset_bist_module(dut.generator) + yield from reset_bist_module(dut.checker) # write yield dut.generator.base.storage.eq(16) yield dut.generator.length.storage.eq(16) for i in range(32): yield - yield dut.generator.start.re.eq(1) - yield - yield dut.generator.start.re.eq(0) + yield from toggle_re(dut.generator.start) for i in range(32): yield while((yield dut.generator.done.status) == 0): @@ -93,9 +89,7 @@ def main_generator(dut): yield dut.checker.length.storage.eq(16) for i in range(32): yield - yield dut.checker.start.re.eq(1) - yield - yield dut.checker.start.re.eq(0) + yield from toggle_re(dut.generator.start) for i in range(32): yield while((yield dut.checker.done.status) == 0): diff --git a/test/bist_tb.py b/test/bist_tb.py index f4fe618..22341bc 100755 --- a/test/bist_tb.py +++ b/test/bist_tb.py @@ -10,7 +10,7 @@ from litedram.common import LiteDRAMWritePort, LiteDRAMReadPort from litedram.frontend.bist import LiteDRAMBISTGenerator from litedram.frontend.bist import LiteDRAMBISTChecker -from test.common import DRAMMemory +from test.common import * class TB(Module): def __init__(self): @@ -20,32 +20,6 @@ class TB(Module): self.submodules.checker = LiteDRAMBISTChecker(self.read_port) -def togglereset(module): - resig = module.reset.re - - # Check that reset isn't set - reval = yield resig - assert not reval, reval - - # Toggle the reset - yield resig.eq(1) - yield - yield resig.eq(0) - yield # Takes 3 clock cycles for the reset to have an effect - yield - yield - yield - yield - yield - - # Check some initial conditions are correct after reset. - started = yield module.core.started - assert started == 0, started - - done = yield module.done.status - assert not done, done - - def main_generator(dut, mem): # Populate memory with random data random.seed(0) @@ -53,7 +27,7 @@ def main_generator(dut, mem): mem.mem[i] = random.randint(0, 2**mem.width) # write - yield from togglereset(dut.generator) + yield from reset_bist_module(dut.generator) yield dut.generator.base.storage.eq(16) yield dut.generator.length.storage.eq(64) @@ -70,7 +44,7 @@ def main_generator(dut, mem): assert done, done # read with no errors - yield from togglereset(dut.checker) + yield from reset_bist_module(dut.checker) errors = yield dut.checker.error_count.status assert errors == 0, errors @@ -78,9 +52,7 @@ def main_generator(dut, mem): yield dut.checker.length.storage.eq(64) for i in range(8): yield - yield dut.checker.start.re.eq(1) - yield - yield dut.checker.start.re.eq(0) + yield from toggle_re(dut.checker.start) for i in range(8): yield while((yield dut.checker.done.status) == 0): @@ -94,7 +66,7 @@ def main_generator(dut, mem): yield # read with one error - yield from togglereset(dut.checker) + yield from reset_bist_module(dut.checker) errors = yield dut.checker.error_count.status assert errors == 0, errors @@ -105,9 +77,7 @@ def main_generator(dut, mem): yield dut.checker.length.storage.eq(64) for i in range(8): yield - yield dut.checker.start.re.eq(1) - yield - yield dut.checker.start.re.eq(0) + yield from toggle_re(dut.checker.start) for i in range(8): yield while((yield dut.checker.done.status) == 0): diff --git a/test/common.py b/test/common.py index e8f0f2d..ac0d2a7 100644 --- a/test/common.py +++ b/test/common.py @@ -1,6 +1,33 @@ from litex.gen import * +def toggle_re(reg): + resig = reg.re + # Check that reset isn't set + reval = yield resig + assert not reval, reval + yield resig.eq(1) + yield + yield resig.eq(0) + + +def reset_bist_module(module): + # Toggle the reset + yield from toggle_re(module.reset) + yield # Takes 5 more clock cycles for the reset to have an effect + yield + yield + yield + yield + + # Check some initial conditions are correct after reset. + started = yield module.core.started + assert started == 0, started + + done = yield module.done.status + assert not done, done + + def seed_to_data(seed, random=True, nbits=32): if nbits == 32: if random: From c0b8d1a71433309b47dd7d847585966fcdf4208d Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Fri, 16 Dec 2016 17:09:21 +0100 Subject: [PATCH 07/12] bist: Adding some documentation. (Plus small formatting cleanup.) --- litedram/frontend/bist.py | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/litedram/frontend/bist.py b/litedram/frontend/bist.py index e595da7..97b14be 100644 --- a/litedram/frontend/bist.py +++ b/litedram/frontend/bist.py @@ -1,3 +1,5 @@ +"""Built In Self Test (BIST) modules for testing liteDRAM functionality.""" + from functools import reduce from operator import xor @@ -11,6 +13,8 @@ from litedram.frontend.dma import LiteDRAMDMAWriter, LiteDRAMDMAReader @CEInserter() class LFSR(Module): + """Linear-Feedback Shift Register to generate a pseudo-random sequence.""" + def __init__(self, n_out, n_state=31, taps=[27, 30]): self.o = Signal(n_out) @@ -32,15 +36,15 @@ class LFSR(Module): @CEInserter() class Counter(Module): - def __init__(self, n_out): - self.o = Signal(n_out) + """Simple incremental counter.""" - # # # - - self.sync += self.o.eq(self.o + 1) + def __init__(self, n_out): + self.o = Signal(n_out) + self.sync += self.o.eq(self.o + 1) class _LiteDRAMBISTGenerator(Module): + def __init__(self, dram_port, random): self.start = Signal() self.done = Signal() @@ -80,6 +84,17 @@ class _LiteDRAMBISTGenerator(Module): class LiteDRAMBISTGenerator(Module, AutoCSR): + """litex module to generate a given pattern in memory.abs + + CSRs: + * reset - Reset the module + * start - Start the checking + * done - The module has completed writing pattern + + * base - DRAM address to start from. + * length - Number of DRAM words to check for. + """ + def __init__(self, dram_port, random=True): self.reset = CSR() self.start = CSR() @@ -122,6 +137,7 @@ class LiteDRAMBISTGenerator(Module, AutoCSR): class _LiteDRAMBISTChecker(Module, AutoCSR): + def __init__(self, dram_port, random): self.start = Signal() self.done = Signal() @@ -188,6 +204,18 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): class LiteDRAMBISTChecker(Module, AutoCSR): + """litex module to check a given pattern in memory. + + CSRs: + * reset - Reset the module + * start - Start the checking + * done - The module has completed checking + + * base - DRAM address to start from. + * length - Number of DRAM words to check for. + * error_count - Number of DRAM words which don't match. + """ + def __init__(self, dram_port, random=True): self.reset = CSR() self.start = CSR() From 8ff2f8779bef2f7b45a576eba2e5a48d0d5262b0 Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Sat, 17 Dec 2016 11:49:22 +0100 Subject: [PATCH 08/12] bist: Adding "halt on error" functionality. Also include ability to see address of error and expected verse actual data values. Extend the test bench to test this functionality. --- litedram/frontend/bist.py | 147 +++++++++++++++++++++++++------------- test/bist_tb.py | 137 +++++++++++++++++++++++++++++++++-- test/common.py | 8 +-- 3 files changed, 232 insertions(+), 60 deletions(-) diff --git a/litedram/frontend/bist.py b/litedram/frontend/bist.py index 97b14be..15eff9a 100644 --- a/litedram/frontend/bist.py +++ b/litedram/frontend/bist.py @@ -140,10 +140,18 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): def __init__(self, dram_port, random): self.start = Signal() - self.done = Signal() + self.base = Signal(dram_port.aw) self.length = Signal(dram_port.aw) + self.halt_on_error = Signal() + self.error_count = Signal(32) + self.error_addr = Signal(dram_port.aw) + + self.error_wanted = Signal(dram_port.dw) + self.error_actual = Signal(dram_port.dw) + + self.done = Signal() # # # @@ -154,66 +162,91 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): else: self.submodules.gen = gen = Counter(dram_port.dw) + self._address_counter = address_counter = Signal(dram_port.aw) + self.started = started = Signal() - address_counter = Signal(dram_port.aw) - address_counter_ce = Signal() - data_counter = Signal(dram_port.aw) - data_counter_ce = Signal() self.sync += [ If(self.start, - started.eq(1) + started.eq(1), + If(self.error_addr != 0, + self.error_addr.eq(0), + address_counter.eq(address_counter+1), + ), ), - If(self.start, - address_counter.eq(0) - ).Elif(address_counter_ce, - address_counter.eq(address_counter + 1) - ), - If(self.start, - data_counter.eq(0), - ).Elif(data_counter_ce, - data_counter.eq(data_counter + 1) - ) ] address_enable = Signal() - self.comb += address_enable.eq(started & (address_counter != (self.length - 1))) - + address_counter_ce = Signal() self.comb += [ + address_enable.eq(started & (address_counter != (self.length - 1))), + address_counter_ce.eq(address_enable & dma.sink.ready), + dma.sink.valid.eq(address_enable), dma.sink.address.eq(self.base + address_counter), - address_counter_ce.eq(address_enable & dma.sink.ready) - ] - - data_enable = Signal() - self.comb += data_enable.eq(started & (data_counter != (self.length - 1))) - - self.comb += [ - gen.ce.eq(dma.source.valid), - dma.source.ready.eq(1) ] self.sync += [ - If(dma.source.valid, + If(address_counter_ce, + address_counter.eq(address_counter + 1) + ), + ] + + self._data_counter = data_counter = Signal(dram_port.aw) + data_enable = Signal() + data_counter_ce = Signal() + self.comb += [ + data_enable.eq(started & (data_counter != address_counter)), + data_counter_ce.eq(data_enable & dma.source.valid), + + dma.source.ready.eq(data_counter_ce), + gen.ce.eq(data_counter_ce), + ] + self.sync += [ + If(data_counter_ce, + data_counter.eq(data_counter + 1), If(dma.source.data != gen.o, - self.error_count.eq(self.error_count + 1) + self.error_count.eq(self.error_count + 1), + self.error_addr.eq(self.base + data_counter), + self.error_wanted.eq(gen.o), + self.error_actual.eq(dma.source.data), + If(self.halt_on_error, + started.eq(0), + ), ) ), ] - self.comb += data_counter_ce.eq(dma.source.valid) - self.comb += self.done.eq(~data_enable & ~address_enable & started) + error = Signal() + self.comb += [ + error.eq(self.halt_on_error & (self.error_addr != 0)), + ] + + self.comb += self.done.eq((~data_enable & ~address_enable & started) | error) class LiteDRAMBISTChecker(Module, AutoCSR): """litex module to check a given pattern in memory. - CSRs: - * reset - Reset the module - * start - Start the checking - * done - The module has completed checking + Attributes + ---------- + reset : in + Reset the module + start : in + Start the checking - * base - DRAM address to start from. - * length - Number of DRAM words to check for. - * error_count - Number of DRAM words which don't match. + base : in + DRAM address to start from. + length : in + Number of DRAM words to check for. + halt_on_error : in + Stop checking at the first error to occur. + + done : out + The module has completed checking + + error_count : out + Number of DRAM words which don't match. + error_addr : out + Address of the last error to occur. """ def __init__(self, dram_port, random=True): @@ -222,7 +255,10 @@ class LiteDRAMBISTChecker(Module, AutoCSR): self.done = CSRStatus() self.base = CSRStorage(dram_port.aw) self.length = CSRStorage(dram_port.aw) + self.halt_on_error = CSRStorage() + self.error_count = CSRStatus(32) + self.error_addr = CSRStatus(dram_port.aw) # # # @@ -231,32 +267,47 @@ class LiteDRAMBISTChecker(Module, AutoCSR): core = ResetInserter()(_LiteDRAMBISTChecker(dram_port, random)) self.submodules.core = ClockDomainsRenamer(cd)(core) + #reset_sync = PulseSynchronizer("sys", cd) reset_sync = BusSynchronizer(1, "sys", cd) start_sync = PulseSynchronizer("sys", cd) - done_sync = BusSynchronizer(1, cd, "sys") - self.submodules += reset_sync, start_sync, done_sync - - base_sync = BusSynchronizer(dram_port.aw, "sys", cd) - length_sync = BusSynchronizer(dram_port.aw, "sys", cd) - error_count_sync = BusSynchronizer(32, cd, "sys") - self.submodules += base_sync, length_sync, error_count_sync - + self.submodules += reset_sync, start_sync self.comb += [ reset_sync.i.eq(self.reset.re), core.reset.eq(reset_sync.o), start_sync.i.eq(self.start.re), core.start.eq(start_sync.o), + ] + done_sync = BusSynchronizer(1, cd, "sys") + self.submodules += done_sync + self.comb += [ done_sync.i.eq(core.done), self.done.status.eq(done_sync.o), + ] + base_sync = BusSynchronizer(dram_port.aw, "sys", cd) + length_sync = BusSynchronizer(dram_port.aw, "sys", cd) + halt_on_error_sync = BusSynchronizer(1, "sys", cd) + self.submodules += base_sync, length_sync, halt_on_error_sync + self.comb += [ base_sync.i.eq(self.base.storage), core.base.eq(base_sync.o), length_sync.i.eq(self.length.storage), core.length.eq(length_sync.o), - error_count_sync.i.eq(core.error_count), - self.error_count.status.eq(error_count_sync.o) + halt_on_error_sync.i.eq(self.halt_on_error.storage), + core.halt_on_error.eq(halt_on_error_sync.o), + ] + + error_count_sync = BusSynchronizer(32, cd, "sys") + error_addr_sync = BusSynchronizer(dram_port.aw, cd, "sys") + self.submodules += error_addr_sync, error_count_sync + self.comb += [ + error_count_sync.i.eq(core.error_count), + self.error_count.status.eq(error_count_sync.o), + + error_addr_sync.i.eq(core.error_addr), + self.error_addr.status.eq(error_addr_sync.o), ] diff --git a/test/bist_tb.py b/test/bist_tb.py index 22341bc..c533633 100755 --- a/test/bist_tb.py +++ b/test/bist_tb.py @@ -16,8 +16,18 @@ class TB(Module): def __init__(self): self.write_port = LiteDRAMWritePort(aw=32, dw=32) self.read_port = LiteDRAMReadPort(aw=32, dw=32) - self.submodules.generator = LiteDRAMBISTGenerator(self.write_port) - self.submodules.checker = LiteDRAMBISTChecker(self.read_port) + self.submodules.generator = LiteDRAMBISTGenerator(self.write_port, random=True) + self.submodules.checker = LiteDRAMBISTChecker(self.read_port, random=True) + + +finished = [] + +def cycle_assert(dut): + while not finished: + addr = yield dut.checker.core._address_counter + data = yield dut.checker.core._data_counter + assert addr >= data, "addr {} >= data {}".format(addr, data) + yield def main_generator(dut, mem): @@ -70,8 +80,9 @@ def main_generator(dut, mem): errors = yield dut.checker.error_count.status assert errors == 0, errors - assert mem.mem[20] != 0, mem.mem[20] - mem.mem[20] = 0 # Make position 20 an error + print("mem.mem[20]", hex(mem.mem[20])) + assert mem.mem[20] == 0xffff000f, hex(mem.mem[20]) + mem.mem[20] = 0x200 # Make position 20 an error yield dut.checker.base.storage.eq(16) yield dut.checker.length.storage.eq(64) @@ -87,17 +98,129 @@ def main_generator(dut, mem): errors = yield dut.checker.error_count.status assert errors == 1, errors + error_addr = yield dut.checker.error_addr.status + assert error_addr == 20, error_addr + yield yield + # read with two errors + yield from reset_bist_module(dut.checker) + errors = yield dut.checker.error_count.status + assert errors == 0, errors + + print("mem.mem[21]", hex(mem.mem[21])) + assert mem.mem[21] == 0xfff1ff1f, hex(mem.mem[21]) + mem.mem[21] = 0x210 # Make position 21 an error + + yield dut.checker.base.storage.eq(16) + yield dut.checker.length.storage.eq(64) + for i in range(8): + yield + yield from toggle_re(dut.checker.start) + for i in range(8): + yield + while((yield dut.checker.done.status) == 0): + yield + done = yield dut.checker.done.status + assert done, done + errors = yield dut.checker.error_count.status + assert errors == 2, errors + + error_addr = yield dut.checker.error_addr.status + assert error_addr == 21, error_addr + + yield + yield + + # read with two errors but halting on the first one + yield from reset_bist_module(dut.checker) + errors = yield dut.checker.error_count.status + assert errors == 0, errors + + yield dut.checker.base.storage.eq(16) + yield dut.checker.length.storage.eq(64) + yield dut.checker.halt_on_error.storage.eq(1) + for i in range(8): + yield + yield from toggle_re(dut.checker.start) + for i in range(8): + yield + while((yield dut.checker.done.status) == 0): + yield + done = yield dut.checker.done.status + assert done, done + started = yield dut.checker.core.started + assert not started, started + for i in range(16): + yield + + errors = yield dut.checker.error_count.status + assert errors == 1, errors + error_addr = yield dut.checker.error_addr.status + assert error_addr == 20, error_addr + error_wanted = yield dut.checker.core.error_wanted + assert error_wanted == 0xffff000f, error_wanted + error_actual = yield dut.checker.core.error_actual + assert error_actual == 0x200, error_actual + + yield from toggle_re(dut.checker.start) + for i in range(8): + yield + while((yield dut.checker.done.status) == 0): + yield + done = yield dut.checker.done.status + assert done, done + started = yield dut.checker.core.started + assert not started, started + for i in range(16): + yield + + errors = yield dut.checker.error_count.status + assert errors == 2, errors + error_addr = yield dut.checker.error_addr.status + assert error_addr == 21, error_addr + error_wanted = yield dut.checker.core.error_wanted + assert error_wanted == 0xfff1ff1f, error_wanted + error_actual = yield dut.checker.core.error_actual + assert error_actual == 0x210, error_actual + + yield from toggle_re(dut.checker.start) + for i in range(8): + yield + while((yield dut.checker.done.status) == 0): + yield + done = yield dut.checker.done.status + assert done, done + started = yield dut.checker.core.started + assert started, started + for i in range(16): + yield + + error_addr = yield dut.checker.error_addr.status + error_wanted = yield dut.checker.core.error_wanted + error_actual = yield dut.checker.core.error_actual + errors = yield dut.checker.error_count.status + assert errors == 2, errors + error_addr = yield dut.checker.error_addr.status + assert error_addr == 0, error_addr + + yield + yield + + finished.append(True) + if __name__ == "__main__": tb = TB() mem = DRAMMemory(32, 128) generators = { - "sys" : [main_generator(tb, mem), - mem.write_generator(tb.write_port), - mem.read_generator(tb.read_port)] + "sys" : [ + main_generator(tb, mem), + mem.write_generator(tb.write_port), + mem.read_generator(tb.read_port), + cycle_assert(tb), + ], } clocks = {"sys": 10} run_simulation(tb, generators, clocks, vcd_name="sim.vcd") diff --git a/test/common.py b/test/common.py index ac0d2a7..802017e 100644 --- a/test/common.py +++ b/test/common.py @@ -14,11 +14,9 @@ def toggle_re(reg): def reset_bist_module(module): # Toggle the reset yield from toggle_re(module.reset) - yield # Takes 5 more clock cycles for the reset to have an effect - yield - yield - yield - yield + # Takes 8 more clock cycles for the reset to have an effect + for i in range(8): + yield # Check some initial conditions are correct after reset. started = yield module.core.started From f1ad8991a4647f9ef9b36d2eba602232517f7adf Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Sat, 17 Dec 2016 12:01:58 +0100 Subject: [PATCH 09/12] bist: Working on improving the names of things. --- litedram/frontend/bist.py | 103 ++++++++++++++++++++++---------------- test/bist_tb.py | 74 +++++++++++++-------------- test/common.py | 4 +- 3 files changed, 100 insertions(+), 81 deletions(-) diff --git a/litedram/frontend/bist.py b/litedram/frontend/bist.py index 15eff9a..bb53b21 100644 --- a/litedram/frontend/bist.py +++ b/litedram/frontend/bist.py @@ -60,13 +60,13 @@ class _LiteDRAMBISTGenerator(Module): else: self.submodules.gen = gen = Counter(dram_port.dw) - self.started = started = Signal() - enable = Signal() + self.running = running = Signal() + not_finished = Signal() counter = Signal(dram_port.aw) - self.comb += enable.eq(started & (counter != (self.length - 1))) + self.comb += not_finished.eq(running & (counter != (self.length - 1))) self.sync += [ If(self.start, - started.eq(1), + running.eq(1), counter.eq(0) ).Elif(gen.ce, counter.eq(counter + 1) @@ -74,12 +74,12 @@ class _LiteDRAMBISTGenerator(Module): ] self.comb += [ - dma.sink.valid.eq(enable), + dma.sink.valid.eq(not_finished), dma.sink.address.eq(self.base + counter), dma.sink.data.eq(gen.o), - gen.ce.eq(enable & dma.sink.ready), + gen.ce.eq(not_finished & dma.sink.ready), - self.done.eq(~enable & started) + self.done.eq(~not_finished & running) ] @@ -145,11 +145,11 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): self.length = Signal(dram_port.aw) self.halt_on_error = Signal() - self.error_count = Signal(32) - self.error_addr = Signal(dram_port.aw) + self.err_count = Signal(32) + self.err_addr = Signal(dram_port.aw) - self.error_wanted = Signal(dram_port.dw) - self.error_actual = Signal(dram_port.dw) + self.err_expect = Signal(dram_port.dw) + self.err_actual = Signal(dram_port.dw) self.done = Signal() @@ -164,24 +164,24 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): self._address_counter = address_counter = Signal(dram_port.aw) - self.started = started = Signal() + self.running = running = Signal() self.sync += [ If(self.start, - started.eq(1), - If(self.error_addr != 0, - self.error_addr.eq(0), + running.eq(1), + If(self.err_addr != 0, + self.err_addr.eq(0), address_counter.eq(address_counter+1), ), ), ] - address_enable = Signal() + address_not_finished = Signal() address_counter_ce = Signal() self.comb += [ - address_enable.eq(started & (address_counter != (self.length - 1))), - address_counter_ce.eq(address_enable & dma.sink.ready), + address_not_finished.eq(running & (address_counter != (self.length - 1))), + address_counter_ce.eq(address_not_finished & dma.sink.ready), - dma.sink.valid.eq(address_enable), + dma.sink.valid.eq(address_not_finished), dma.sink.address.eq(self.base + address_counter), ] self.sync += [ @@ -191,11 +191,11 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): ] self._data_counter = data_counter = Signal(dram_port.aw) - data_enable = Signal() + data_not_finished = Signal() data_counter_ce = Signal() self.comb += [ - data_enable.eq(started & (data_counter != address_counter)), - data_counter_ce.eq(data_enable & dma.source.valid), + data_not_finished.eq(running & (data_counter != address_counter)), + data_counter_ce.eq(data_not_finished & dma.source.valid), dma.source.ready.eq(data_counter_ce), gen.ce.eq(data_counter_ce), @@ -204,12 +204,12 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): If(data_counter_ce, data_counter.eq(data_counter + 1), If(dma.source.data != gen.o, - self.error_count.eq(self.error_count + 1), - self.error_addr.eq(self.base + data_counter), - self.error_wanted.eq(gen.o), - self.error_actual.eq(dma.source.data), + self.err_count.eq(self.err_count + 1), + self.err_addr.eq(self.base + data_counter), + self.err_expect.eq(gen.o), + self.err_actual.eq(dma.source.data), If(self.halt_on_error, - started.eq(0), + running.eq(0), ), ) ), @@ -217,10 +217,10 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): error = Signal() self.comb += [ - error.eq(self.halt_on_error & (self.error_addr != 0)), + error.eq(self.halt_on_error & (self.err_addr != 0)), ] - self.comb += self.done.eq((~data_enable & ~address_enable & started) | error) + self.comb += self.done.eq((~data_not_finished & ~address_not_finished & running) | error) class LiteDRAMBISTChecker(Module, AutoCSR): @@ -243,22 +243,31 @@ class LiteDRAMBISTChecker(Module, AutoCSR): done : out The module has completed checking - error_count : out + err_count : out Number of DRAM words which don't match. - error_addr : out + + err_addr : out Address of the last error to occur. + err_expect : out + Expected data value on the last error. + err_actual : out + Actual data value on the last error. """ def __init__(self, dram_port, random=True): self.reset = CSR() self.start = CSR() - self.done = CSRStatus() + self.base = CSRStorage(dram_port.aw) self.length = CSRStorage(dram_port.aw) self.halt_on_error = CSRStorage() - self.error_count = CSRStatus(32) - self.error_addr = CSRStatus(dram_port.aw) + self.done = CSRStatus() + + self.err_count = CSRStatus(32) + self.err_addr = CSRStatus(dram_port.aw) + self.err_expect = CSRStatus(dram_port.dw) + self.err_actual = CSRStatus(dram_port.dw) # # # @@ -301,13 +310,23 @@ class LiteDRAMBISTChecker(Module, AutoCSR): core.halt_on_error.eq(halt_on_error_sync.o), ] - error_count_sync = BusSynchronizer(32, cd, "sys") - error_addr_sync = BusSynchronizer(dram_port.aw, cd, "sys") - self.submodules += error_addr_sync, error_count_sync - self.comb += [ - error_count_sync.i.eq(core.error_count), - self.error_count.status.eq(error_count_sync.o), + err_count_sync = BusSynchronizer(32, cd, "sys") + err_addr_sync = BusSynchronizer(dram_port.aw, cd, "sys") + err_expect_sync = BusSynchronizer(dram_port.dw, cd, "sys") + err_actual_sync = BusSynchronizer(dram_port.dw, cd, "sys") - error_addr_sync.i.eq(core.error_addr), - self.error_addr.status.eq(error_addr_sync.o), + self.submodules += err_addr_sync, err_count_sync, err_expect_sync, err_actual_sync + + self.comb += [ + err_count_sync.i.eq(core.err_count), + self.err_count.status.eq(err_count_sync.o), + + err_addr_sync.i.eq(core.err_addr), + self.err_addr.status.eq(err_addr_sync.o), + + err_expect_sync.i.eq(core.err_expect), + self.err_expect.status.eq(err_expect_sync.o), + + err_actual_sync.i.eq(core.err_actual), + self.err_actual.status.eq(err_actual_sync.o), ] diff --git a/test/bist_tb.py b/test/bist_tb.py index c533633..a142fb0 100755 --- a/test/bist_tb.py +++ b/test/bist_tb.py @@ -55,7 +55,7 @@ def main_generator(dut, mem): # read with no errors yield from reset_bist_module(dut.checker) - errors = yield dut.checker.error_count.status + errors = yield dut.checker.err_count.status assert errors == 0, errors yield dut.checker.base.storage.eq(16) @@ -69,7 +69,7 @@ def main_generator(dut, mem): yield done = yield dut.checker.done.status assert done, done - errors = yield dut.checker.error_count.status + errors = yield dut.checker.err_count.status assert errors == 0, errors yield @@ -77,7 +77,7 @@ def main_generator(dut, mem): # read with one error yield from reset_bist_module(dut.checker) - errors = yield dut.checker.error_count.status + errors = yield dut.checker.err_count.status assert errors == 0, errors print("mem.mem[20]", hex(mem.mem[20])) @@ -95,18 +95,18 @@ def main_generator(dut, mem): yield done = yield dut.checker.done.status assert done, done - errors = yield dut.checker.error_count.status + errors = yield dut.checker.err_count.status assert errors == 1, errors - error_addr = yield dut.checker.error_addr.status - assert error_addr == 20, error_addr + err_addr = yield dut.checker.err_addr.status + assert err_addr == 20, err_addr yield yield # read with two errors yield from reset_bist_module(dut.checker) - errors = yield dut.checker.error_count.status + errors = yield dut.checker.err_count.status assert errors == 0, errors print("mem.mem[21]", hex(mem.mem[21])) @@ -124,18 +124,18 @@ def main_generator(dut, mem): yield done = yield dut.checker.done.status assert done, done - errors = yield dut.checker.error_count.status + errors = yield dut.checker.err_count.status assert errors == 2, errors - error_addr = yield dut.checker.error_addr.status - assert error_addr == 21, error_addr + err_addr = yield dut.checker.err_addr.status + assert err_addr == 21, err_addr yield yield # read with two errors but halting on the first one yield from reset_bist_module(dut.checker) - errors = yield dut.checker.error_count.status + errors = yield dut.checker.err_count.status assert errors == 0, errors yield dut.checker.base.storage.eq(16) @@ -150,19 +150,19 @@ def main_generator(dut, mem): yield done = yield dut.checker.done.status assert done, done - started = yield dut.checker.core.started - assert not started, started + running = yield dut.checker.core.running + assert not running, running for i in range(16): yield - errors = yield dut.checker.error_count.status + errors = yield dut.checker.err_count.status assert errors == 1, errors - error_addr = yield dut.checker.error_addr.status - assert error_addr == 20, error_addr - error_wanted = yield dut.checker.core.error_wanted - assert error_wanted == 0xffff000f, error_wanted - error_actual = yield dut.checker.core.error_actual - assert error_actual == 0x200, error_actual + err_addr = yield dut.checker.err_addr.status + assert err_addr == 20, err_addr + err_expect = yield dut.checker.core.err_expect + assert err_expect == 0xffff000f, err_expect + err_actual = yield dut.checker.core.err_actual + assert err_actual == 0x200, err_actual yield from toggle_re(dut.checker.start) for i in range(8): @@ -171,19 +171,19 @@ def main_generator(dut, mem): yield done = yield dut.checker.done.status assert done, done - started = yield dut.checker.core.started - assert not started, started + running = yield dut.checker.core.running + assert not running, running for i in range(16): yield - errors = yield dut.checker.error_count.status + errors = yield dut.checker.err_count.status assert errors == 2, errors - error_addr = yield dut.checker.error_addr.status - assert error_addr == 21, error_addr - error_wanted = yield dut.checker.core.error_wanted - assert error_wanted == 0xfff1ff1f, error_wanted - error_actual = yield dut.checker.core.error_actual - assert error_actual == 0x210, error_actual + err_addr = yield dut.checker.err_addr.status + assert err_addr == 21, err_addr + err_expect = yield dut.checker.core.err_expect + assert err_expect == 0xfff1ff1f, err_expect + err_actual = yield dut.checker.core.err_actual + assert err_actual == 0x210, err_actual yield from toggle_re(dut.checker.start) for i in range(8): @@ -192,18 +192,18 @@ def main_generator(dut, mem): yield done = yield dut.checker.done.status assert done, done - started = yield dut.checker.core.started - assert started, started + running = yield dut.checker.core.running + assert running, running for i in range(16): yield - error_addr = yield dut.checker.error_addr.status - error_wanted = yield dut.checker.core.error_wanted - error_actual = yield dut.checker.core.error_actual - errors = yield dut.checker.error_count.status + err_addr = yield dut.checker.err_addr.status + err_expect = yield dut.checker.core.err_expect + err_actual = yield dut.checker.core.err_actual + errors = yield dut.checker.err_count.status assert errors == 2, errors - error_addr = yield dut.checker.error_addr.status - assert error_addr == 0, error_addr + err_addr = yield dut.checker.err_addr.status + assert err_addr == 0, err_addr yield yield diff --git a/test/common.py b/test/common.py index 802017e..204114f 100644 --- a/test/common.py +++ b/test/common.py @@ -19,8 +19,8 @@ def reset_bist_module(module): yield # Check some initial conditions are correct after reset. - started = yield module.core.started - assert started == 0, started + running = yield module.core.running + assert running == 0, running done = yield module.done.status assert not done, done From fcc1d5059e96b16f6bbdf30e3d48939efc16f3ca Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Sat, 17 Dec 2016 12:07:48 +0100 Subject: [PATCH 10/12] bist: Improving documentation a bit. --- litedram/frontend/bist.py | 51 ++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/litedram/frontend/bist.py b/litedram/frontend/bist.py index bb53b21..f8c6550 100644 --- a/litedram/frontend/bist.py +++ b/litedram/frontend/bist.py @@ -13,7 +13,22 @@ from litedram.frontend.dma import LiteDRAMDMAWriter, LiteDRAMDMAReader @CEInserter() class LFSR(Module): - """Linear-Feedback Shift Register to generate a pseudo-random sequence.""" + """Linear-Feedback Shift Register to generate a pseudo-random sequence. + + Parameters + ---------- + n_out : int + Width of the output data signal. + n_state : int + ??? + taps : list of int + ??? + + Attributes + ---------- + o : in + Output data + """ def __init__(self, n_out, n_state=31, taps=[27, 30]): self.o = Signal(n_out) @@ -36,7 +51,18 @@ class LFSR(Module): @CEInserter() class Counter(Module): - """Simple incremental counter.""" + """Simple incremental counter. + + Parameters + ---------- + n_out : int + Width of the output data signal. + + Attributes + ---------- + o : in + Output data + """ def __init__(self, n_out): self.o = Signal(n_out) @@ -86,13 +112,20 @@ class _LiteDRAMBISTGenerator(Module): class LiteDRAMBISTGenerator(Module, AutoCSR): """litex module to generate a given pattern in memory.abs - CSRs: - * reset - Reset the module - * start - Start the checking - * done - The module has completed writing pattern + Attributes + ---------- + reset : in + Reset the module. + start : in + Start the generation. - * base - DRAM address to start from. - * length - Number of DRAM words to check for. + base : in + DRAM address to start from. + length : in + Number of DRAM words to write. + + done : out + The module has completed writing the pattern. """ def __init__(self, dram_port, random=True): @@ -236,7 +269,7 @@ class LiteDRAMBISTChecker(Module, AutoCSR): base : in DRAM address to start from. length : in - Number of DRAM words to check for. + Number of DRAM words to check. halt_on_error : in Stop checking at the first error to occur. From 70a333628b0f611f8f7f205d883b745706aba708 Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Sat, 17 Dec 2016 12:15:32 +0100 Subject: [PATCH 11/12] dma: Adding some documentation. --- litedram/frontend/dma.py | 47 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/litedram/frontend/dma.py b/litedram/frontend/dma.py index 13e1b7e..0a4b522 100644 --- a/litedram/frontend/dma.py +++ b/litedram/frontend/dma.py @@ -1,9 +1,37 @@ +"""Direct Memory Access (DMA) reader and writer modules.""" + from litex.gen import * from litex.soc.interconnect import stream class LiteDRAMDMAReader(Module): + """Read data from DRAM memory. + + For every address written to the sink, one DRAM word will be produced on + the source. + + Parameters + ---------- + port : dram_port + Port on the DRAM memory controller to read from. + + fifo_depth : int + How many request results the output FIFO can contain (and thus how many + read requests can be outstanding at once). + + fifo_buffered : bool + ??? + + Attributes + ---------- + sink : Record("address") + Sink for DRAM addresses to be read. + + source : Record("data") + Source for DRAM word results from reading. + """ + def __init__(self, port, fifo_depth=16, fifo_buffered=False): self.sink = sink = stream.Endpoint([("address", port.aw)]) self.source = source = stream.Endpoint([("data", port.dw)]) @@ -48,6 +76,25 @@ class LiteDRAMDMAReader(Module): class LiteDRAMDMAWriter(Module): + """Write data to DRAM memory. + + Parameters + ---------- + port : dram_port + Port on the DRAM memory controller to write to. + + fifo_depth : int + How many requests the input FIFO can contain (and thus how many write + requests can be outstanding at once). + + fifo_buffered : bool + ??? + + Attributes + ---------- + sink : Record("address", "data") + Sink for DRAM addresses and DRAM data word to be written too. + """ def __init__(self, port, fifo_depth=16, fifo_buffered=False): self.sink = sink = stream.Endpoint([("address", port.aw), ("data", port.dw)]) From bc75d4f3d5a21aa9bd5a81273ae8eb6cc61d61de Mon Sep 17 00:00:00 2001 From: Tim 'mithro' Ansell Date: Sat, 17 Dec 2016 17:49:47 +0100 Subject: [PATCH 12/12] bist: Reworking as suggested by Florent. --- litedram/frontend/bist.py | 120 +++++++++++++------------------------- test/bist_tb.py | 78 ++++++++----------------- 2 files changed, 65 insertions(+), 133 deletions(-) diff --git a/litedram/frontend/bist.py b/litedram/frontend/bist.py index f8c6550..059353b 100644 --- a/litedram/frontend/bist.py +++ b/litedram/frontend/bist.py @@ -82,9 +82,9 @@ class _LiteDRAMBISTGenerator(Module): self.submodules.dma = dma = LiteDRAMDMAWriter(dram_port) if random: - self.submodules.gen = gen = LFSR(dram_port.dw) + self.submodules.gen = gen = LFSR(dram_port.dw) else: - self.submodules.gen = gen = Counter(dram_port.dw) + self.submodules.gen = gen = Counter(dram_port.dw) self.running = running = Signal() not_finished = Signal() @@ -176,38 +176,16 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): self.base = Signal(dram_port.aw) self.length = Signal(dram_port.aw) - self.halt_on_error = Signal() - self.err_count = Signal(32) - self.err_addr = Signal(dram_port.aw) - - self.err_expect = Signal(dram_port.dw) - self.err_actual = Signal(dram_port.dw) + self.error_count = Signal(32) + self.running = running = Signal() self.done = Signal() - # # # - self.submodules.dma = dma = LiteDRAMDMAReader(dram_port) - if random: - self.submodules.gen = gen = LFSR(dram_port.dw) - else: - self.submodules.gen = gen = Counter(dram_port.dw) - + # Address generation self._address_counter = address_counter = Signal(dram_port.aw) - - self.running = running = Signal() - self.sync += [ - If(self.start, - running.eq(1), - If(self.err_addr != 0, - self.err_addr.eq(0), - address_counter.eq(address_counter+1), - ), - ), - ] - address_not_finished = Signal() address_counter_ce = Signal() self.comb += [ @@ -223,6 +201,7 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): ), ] + # Data receiving self._data_counter = data_counter = Signal(dram_port.aw) data_not_finished = Signal() data_counter_ce = Signal() @@ -231,29 +210,40 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): data_counter_ce.eq(data_not_finished & dma.source.valid), dma.source.ready.eq(data_counter_ce), - gen.ce.eq(data_counter_ce), ] self.sync += [ - If(data_counter_ce, - data_counter.eq(data_counter + 1), - If(dma.source.data != gen.o, - self.err_count.eq(self.err_count + 1), - self.err_addr.eq(self.base + data_counter), - self.err_expect.eq(gen.o), - self.err_actual.eq(dma.source.data), - If(self.halt_on_error, - running.eq(0), - ), - ) - ), + If(data_counter_ce, data_counter.eq(data_counter + 1)), ] - error = Signal() + # Data checking + if random: + self.submodules.gen = gen = LFSR(dram_port.dw) + else: + self.submodules.gen = gen = Counter(dram_port.dw) self.comb += [ - error.eq(self.halt_on_error & (self.err_addr != 0)), + gen.ce.eq(data_counter_ce), ] - self.comb += self.done.eq((~data_not_finished & ~address_not_finished & running) | error) + self.expected = expected = Signal(dram_port.dw) + self.comb += [ + expected.eq(gen.o), + ] + self.actual = actual = Signal(dram_port.dw) + self.comb += [ + actual.eq(dma.source.data), + ] + + self.error = error = Signal() + self.comb += [ + error.eq(data_counter_ce & (expected != actual)), + ] + self.sync += [ + If(error, self.error_count.eq(self.error_count + 1)), + ] + + # States + self.sync += If(self.start, running.eq(1)) + self.comb += self.done.eq(~data_not_finished & ~address_not_finished & running) class LiteDRAMBISTChecker(Module, AutoCSR): @@ -270,21 +260,12 @@ class LiteDRAMBISTChecker(Module, AutoCSR): DRAM address to start from. length : in Number of DRAM words to check. - halt_on_error : in - Stop checking at the first error to occur. done : out The module has completed checking - err_count : out + error_count : out Number of DRAM words which don't match. - - err_addr : out - Address of the last error to occur. - err_expect : out - Expected data value on the last error. - err_actual : out - Actual data value on the last error. """ def __init__(self, dram_port, random=True): @@ -297,10 +278,7 @@ class LiteDRAMBISTChecker(Module, AutoCSR): self.done = CSRStatus() - self.err_count = CSRStatus(32) - self.err_addr = CSRStatus(dram_port.aw) - self.err_expect = CSRStatus(dram_port.dw) - self.err_actual = CSRStatus(dram_port.dw) + self.error_count = CSRStatus(32) # # # @@ -330,36 +308,18 @@ class LiteDRAMBISTChecker(Module, AutoCSR): base_sync = BusSynchronizer(dram_port.aw, "sys", cd) length_sync = BusSynchronizer(dram_port.aw, "sys", cd) - halt_on_error_sync = BusSynchronizer(1, "sys", cd) - self.submodules += base_sync, length_sync, halt_on_error_sync + self.submodules += base_sync, length_sync self.comb += [ base_sync.i.eq(self.base.storage), core.base.eq(base_sync.o), length_sync.i.eq(self.length.storage), core.length.eq(length_sync.o), - - halt_on_error_sync.i.eq(self.halt_on_error.storage), - core.halt_on_error.eq(halt_on_error_sync.o), ] - err_count_sync = BusSynchronizer(32, cd, "sys") - err_addr_sync = BusSynchronizer(dram_port.aw, cd, "sys") - err_expect_sync = BusSynchronizer(dram_port.dw, cd, "sys") - err_actual_sync = BusSynchronizer(dram_port.dw, cd, "sys") - - self.submodules += err_addr_sync, err_count_sync, err_expect_sync, err_actual_sync - + error_count_sync = BusSynchronizer(32, cd, "sys") + self.submodules += error_count_sync self.comb += [ - err_count_sync.i.eq(core.err_count), - self.err_count.status.eq(err_count_sync.o), - - err_addr_sync.i.eq(core.err_addr), - self.err_addr.status.eq(err_addr_sync.o), - - err_expect_sync.i.eq(core.err_expect), - self.err_expect.status.eq(err_expect_sync.o), - - err_actual_sync.i.eq(core.err_actual), - self.err_actual.status.eq(err_actual_sync.o), + error_count_sync.i.eq(core.error_count), + self.error_count.status.eq(error_count_sync.o), ] diff --git a/test/bist_tb.py b/test/bist_tb.py index a142fb0..6fedf6f 100755 --- a/test/bist_tb.py +++ b/test/bist_tb.py @@ -55,7 +55,7 @@ def main_generator(dut, mem): # read with no errors yield from reset_bist_module(dut.checker) - errors = yield dut.checker.err_count.status + errors = yield dut.checker.error_count.status assert errors == 0, errors yield dut.checker.base.storage.eq(16) @@ -69,7 +69,7 @@ def main_generator(dut, mem): yield done = yield dut.checker.done.status assert done, done - errors = yield dut.checker.err_count.status + errors = yield dut.checker.error_count.status assert errors == 0, errors yield @@ -77,7 +77,7 @@ def main_generator(dut, mem): # read with one error yield from reset_bist_module(dut.checker) - errors = yield dut.checker.err_count.status + errors = yield dut.checker.error_count.status assert errors == 0, errors print("mem.mem[20]", hex(mem.mem[20])) @@ -95,18 +95,15 @@ def main_generator(dut, mem): yield done = yield dut.checker.done.status assert done, done - errors = yield dut.checker.err_count.status + errors = yield dut.checker.error_count.status assert errors == 1, errors - err_addr = yield dut.checker.err_addr.status - assert err_addr == 20, err_addr - yield yield # read with two errors yield from reset_bist_module(dut.checker) - errors = yield dut.checker.err_count.status + errors = yield dut.checker.error_count.status assert errors == 0, errors print("mem.mem[21]", hex(mem.mem[21])) @@ -124,70 +121,50 @@ def main_generator(dut, mem): yield done = yield dut.checker.done.status assert done, done - errors = yield dut.checker.err_count.status + errors = yield dut.checker.error_count.status assert errors == 2, errors - err_addr = yield dut.checker.err_addr.status - assert err_addr == 21, err_addr - yield yield # read with two errors but halting on the first one yield from reset_bist_module(dut.checker) - errors = yield dut.checker.err_count.status + errors = yield dut.checker.error_count.status assert errors == 0, errors yield dut.checker.base.storage.eq(16) yield dut.checker.length.storage.eq(64) - yield dut.checker.halt_on_error.storage.eq(1) for i in range(8): yield yield from toggle_re(dut.checker.start) for i in range(8): yield - while((yield dut.checker.done.status) == 0): - yield - done = yield dut.checker.done.status - assert done, done - running = yield dut.checker.core.running - assert not running, running - for i in range(16): + while((yield dut.checker.core.error) == 0): yield - errors = yield dut.checker.err_count.status - assert errors == 1, errors - err_addr = yield dut.checker.err_addr.status + err_addr = yield dut.checker.core._data_counter + dut.checker.core.base assert err_addr == 20, err_addr - err_expect = yield dut.checker.core.err_expect - assert err_expect == 0xffff000f, err_expect - err_actual = yield dut.checker.core.err_actual + err_expect = yield dut.checker.core.expected + assert err_expect == 0xffff000f, hex(err_expect) + err_actual = yield dut.checker.core.actual assert err_actual == 0x200, err_actual + yield + errors = yield dut.checker.core.error_count + assert errors == 1, errors - yield from toggle_re(dut.checker.start) - for i in range(8): - yield - while((yield dut.checker.done.status) == 0): - yield - done = yield dut.checker.done.status - assert done, done - running = yield dut.checker.core.running - assert not running, running - for i in range(16): + while((yield dut.checker.core.error) == 0): yield - errors = yield dut.checker.err_count.status - assert errors == 2, errors - err_addr = yield dut.checker.err_addr.status + err_addr = yield dut.checker.core._data_counter + dut.checker.core.base assert err_addr == 21, err_addr - err_expect = yield dut.checker.core.err_expect - assert err_expect == 0xfff1ff1f, err_expect - err_actual = yield dut.checker.core.err_actual - assert err_actual == 0x210, err_actual + err_expect = yield dut.checker.core.expected + assert err_expect == 0xfff1ff1f, hex(err_expect) + err_actual = yield dut.checker.core.actual + assert err_actual == 0x210, hex(err_actual) + yield + errors = yield dut.checker.core.error_count + assert errors == 2, errors - yield from toggle_re(dut.checker.start) - for i in range(8): - yield while((yield dut.checker.done.status) == 0): yield done = yield dut.checker.done.status @@ -197,13 +174,8 @@ def main_generator(dut, mem): for i in range(16): yield - err_addr = yield dut.checker.err_addr.status - err_expect = yield dut.checker.core.err_expect - err_actual = yield dut.checker.core.err_actual - errors = yield dut.checker.err_count.status + errors = yield dut.checker.error_count.status assert errors == 2, errors - err_addr = yield dut.checker.err_addr.status - assert err_addr == 0, err_addr yield yield