Merge remote-tracking branch 'upstream/master' into bist
This commit is contained in:
commit
e21b45b608
|
@ -45,7 +45,7 @@ class LFSR(Module):
|
||||||
|
|
||||||
self.sync += [
|
self.sync += [
|
||||||
state.eq(Cat(*curval[:n_state])),
|
state.eq(Cat(*curval[:n_state])),
|
||||||
self.o.eq(Cat(*curval))
|
self.o.eq(Cat(*curval)),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,6 +66,9 @@ class Counter(Module):
|
||||||
|
|
||||||
def __init__(self, n_out):
|
def __init__(self, n_out):
|
||||||
self.o = Signal(n_out)
|
self.o = Signal(n_out)
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
self.sync += self.o.eq(self.o + 1)
|
self.sync += self.o.eq(self.o + 1)
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,32 +83,36 @@ class _LiteDRAMBISTGenerator(Module):
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
self.submodules.dma = dma = LiteDRAMDMAWriter(dram_port)
|
self.submodules.dma = dma = LiteDRAMDMAWriter(dram_port)
|
||||||
|
gen_cls = LFSR if random else Counter
|
||||||
|
self.submodules.gen = gen = gen_cls(dram_port.dw)
|
||||||
|
|
||||||
if random:
|
cmd_counter = Signal(dram_port.aw)
|
||||||
self.submodules.gen = gen = LFSR(dram_port.dw)
|
|
||||||
else:
|
|
||||||
self.submodules.gen = gen = Counter(dram_port.dw)
|
|
||||||
|
|
||||||
self.running = running = Signal()
|
fsm = FSM(reset_state="IDLE")
|
||||||
not_finished = Signal()
|
self.submodules += fsm
|
||||||
counter = Signal(dram_port.aw)
|
|
||||||
self.comb += not_finished.eq(running & (counter != (self.length - 1)))
|
fsm.act("IDLE",
|
||||||
self.sync += [
|
|
||||||
If(self.start,
|
If(self.start,
|
||||||
running.eq(1),
|
NextValue(cmd_counter, 0),
|
||||||
counter.eq(0)
|
NextState("RUN"),
|
||||||
).Elif(gen.ce,
|
),
|
||||||
counter.eq(counter + 1)
|
)
|
||||||
)
|
fsm.act("RUN",
|
||||||
]
|
dma.sink.valid.eq(1),
|
||||||
|
If(dma.sink.ready,
|
||||||
|
gen.ce.eq(1),
|
||||||
|
NextValue(cmd_counter, cmd_counter + 1),
|
||||||
|
If(cmd_counter == (self.length-1),
|
||||||
|
NextState("DONE"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
fsm.act("DONE",
|
||||||
|
self.done.eq(1),
|
||||||
|
)
|
||||||
self.comb += [
|
self.comb += [
|
||||||
dma.sink.valid.eq(not_finished),
|
dma.sink.address.eq(self.base + cmd_counter),
|
||||||
dma.sink.address.eq(self.base + counter),
|
|
||||||
dma.sink.data.eq(gen.o),
|
dma.sink.data.eq(gen.o),
|
||||||
gen.ce.eq(not_finished & dma.sink.ready),
|
|
||||||
|
|
||||||
self.done.eq(~not_finished & running)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -144,28 +151,31 @@ class LiteDRAMBISTGenerator(Module, AutoCSR):
|
||||||
|
|
||||||
reset_sync = BusSynchronizer(1, "sys", cd)
|
reset_sync = BusSynchronizer(1, "sys", cd)
|
||||||
start_sync = PulseSynchronizer("sys", cd)
|
start_sync = PulseSynchronizer("sys", cd)
|
||||||
done_sync = BusSynchronizer(1, cd, "sys")
|
self.submodules += reset_sync, start_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)
|
|
||||||
self.submodules += base_sync, length_sync
|
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
reset_sync.i.eq(self.reset.re),
|
reset_sync.i.eq(self.reset.re),
|
||||||
core.reset.eq(reset_sync.o),
|
core.reset.eq(reset_sync.o),
|
||||||
|
|
||||||
start_sync.i.eq(self.start.re),
|
start_sync.i.eq(self.start.re),
|
||||||
core.start.eq(start_sync.o),
|
core.start.eq(start_sync.o),
|
||||||
|
]
|
||||||
|
|
||||||
|
done_sync = BusSynchronizer(1, cd, "sys")
|
||||||
|
self.submodules += done_sync
|
||||||
|
self.comb += [
|
||||||
done_sync.i.eq(core.done),
|
done_sync.i.eq(core.done),
|
||||||
self.done.status.eq(done_sync.o),
|
self.done.status.eq(done_sync.o),
|
||||||
|
]
|
||||||
|
|
||||||
|
base_sync = BusSynchronizer(dram_port.aw, "sys", cd)
|
||||||
|
length_sync = BusSynchronizer(dram_port.aw, "sys", cd)
|
||||||
|
self.submodules += base_sync, length_sync
|
||||||
|
self.comb += [
|
||||||
base_sync.i.eq(self.base.storage),
|
base_sync.i.eq(self.base.storage),
|
||||||
core.base.eq(base_sync.o),
|
core.base.eq(base_sync.o),
|
||||||
|
|
||||||
length_sync.i.eq(self.length.storage),
|
length_sync.i.eq(self.length.storage),
|
||||||
core.length.eq(length_sync.o)
|
core.length.eq(length_sync.o),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -173,77 +183,80 @@ class _LiteDRAMBISTChecker(Module, AutoCSR):
|
||||||
|
|
||||||
def __init__(self, dram_port, random):
|
def __init__(self, dram_port, random):
|
||||||
self.start = Signal()
|
self.start = Signal()
|
||||||
|
self.done = Signal()
|
||||||
|
|
||||||
self.base = Signal(dram_port.aw)
|
self.base = Signal(dram_port.aw)
|
||||||
self.length = Signal(dram_port.aw)
|
self.length = Signal(dram_port.aw)
|
||||||
|
|
||||||
self.error_count = Signal(32)
|
self.err_count = Signal(32)
|
||||||
|
|
||||||
self.running = running = Signal()
|
# # #
|
||||||
self.done = Signal()
|
|
||||||
|
|
||||||
self.submodules.dma = dma = LiteDRAMDMAReader(dram_port)
|
self.submodules.dma = dma = LiteDRAMDMAReader(dram_port)
|
||||||
|
gen_cls = LFSR if random else Counter
|
||||||
|
self.submodules.gen = gen = gen_cls(dram_port.dw)
|
||||||
|
|
||||||
# Address generation
|
# address
|
||||||
self._address_counter = address_counter = Signal(dram_port.aw)
|
self._cmd_counter = cmd_counter = Signal(dram_port.aw)
|
||||||
address_not_finished = Signal()
|
cmd_fsm = FSM(reset_state="IDLE")
|
||||||
address_counter_ce = Signal()
|
self.submodules += cmd_fsm
|
||||||
self.comb += [
|
|
||||||
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_not_finished),
|
cmd_fsm.act("IDLE",
|
||||||
dma.sink.address.eq(self.base + address_counter),
|
If(self.start,
|
||||||
]
|
NextValue(cmd_counter, 0),
|
||||||
self.sync += [
|
NextState("RUN"),
|
||||||
If(address_counter_ce,
|
|
||||||
address_counter.eq(address_counter + 1)
|
|
||||||
),
|
),
|
||||||
]
|
)
|
||||||
|
cmd_fsm.act("RUN",
|
||||||
|
dma.sink.valid.eq(1),
|
||||||
|
If(dma.sink.ready,
|
||||||
|
NextValue(cmd_counter, cmd_counter + 1),
|
||||||
|
If(cmd_counter == (self.length-1),
|
||||||
|
NextState("DONE")
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
cmd_fsm.act("DONE")
|
||||||
|
self.comb += dma.sink.address.eq(self.base + cmd_counter)
|
||||||
|
|
||||||
# Data receiving
|
# data
|
||||||
self._data_counter = data_counter = Signal(dram_port.aw)
|
self._data_counter = data_counter = Signal(dram_port.aw)
|
||||||
data_not_finished = Signal()
|
data_fsm = FSM(reset_state="IDLE")
|
||||||
data_counter_ce = Signal()
|
self.submodules += data_fsm
|
||||||
self.comb += [
|
|
||||||
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),
|
|
||||||
]
|
|
||||||
self.sync += [
|
|
||||||
If(data_counter_ce, data_counter.eq(data_counter + 1)),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Data checking
|
|
||||||
if random:
|
|
||||||
self.submodules.gen = gen = LFSR(dram_port.dw)
|
|
||||||
else:
|
|
||||||
self.submodules.gen = gen = Counter(dram_port.dw)
|
|
||||||
self.comb += [
|
|
||||||
gen.ce.eq(data_counter_ce),
|
|
||||||
]
|
|
||||||
|
|
||||||
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.error = error = Signal()
|
||||||
|
self.actual = actual = Signal(dram_port.aw)
|
||||||
|
self.expect = expect = Signal(dram_port.aw)
|
||||||
self.comb += [
|
self.comb += [
|
||||||
error.eq(data_counter_ce & (expected != actual)),
|
actual.eq(dma.source.data),
|
||||||
]
|
expect.eq(gen.o),
|
||||||
self.sync += [
|
error.eq(dma.source.valid & (expect != actual)),
|
||||||
If(error, self.error_count.eq(self.error_count + 1)),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# States
|
data_fsm.act("IDLE",
|
||||||
self.sync += If(self.start, running.eq(1))
|
If(self.start,
|
||||||
self.comb += self.done.eq(~data_not_finished & ~address_not_finished & running)
|
NextValue(data_counter, 0),
|
||||||
|
NextValue(self.err_count, 0),
|
||||||
|
NextState("RUN"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
data_fsm.act("RUN",
|
||||||
|
dma.source.ready.eq(1),
|
||||||
|
If(dma.source.valid,
|
||||||
|
gen.ce.eq(1),
|
||||||
|
NextValue(data_counter, data_counter + 1),
|
||||||
|
If(error,
|
||||||
|
NextValue(self.err_count, self.err_count + 1),
|
||||||
|
),
|
||||||
|
If(data_counter == (self.length-1),
|
||||||
|
NextState("DONE"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
data_fsm.act("DONE")
|
||||||
|
|
||||||
|
self.comb += self.done.eq(
|
||||||
|
cmd_fsm.ongoing("DONE") & data_fsm.ongoing("DONE"))
|
||||||
|
|
||||||
|
|
||||||
class LiteDRAMBISTChecker(Module, AutoCSR):
|
class LiteDRAMBISTChecker(Module, AutoCSR):
|
||||||
|
@ -264,7 +277,7 @@ class LiteDRAMBISTChecker(Module, AutoCSR):
|
||||||
done : out
|
done : out
|
||||||
The module has completed checking
|
The module has completed checking
|
||||||
|
|
||||||
error_count : out
|
err_count : out
|
||||||
Number of DRAM words which don't match.
|
Number of DRAM words which don't match.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -274,11 +287,9 @@ class LiteDRAMBISTChecker(Module, AutoCSR):
|
||||||
|
|
||||||
self.base = CSRStorage(dram_port.aw)
|
self.base = CSRStorage(dram_port.aw)
|
||||||
self.length = CSRStorage(dram_port.aw)
|
self.length = CSRStorage(dram_port.aw)
|
||||||
self.halt_on_error = CSRStorage()
|
|
||||||
|
|
||||||
self.done = CSRStatus()
|
self.done = CSRStatus()
|
||||||
|
self.err_count = CSRStatus(32)
|
||||||
self.error_count = CSRStatus(32)
|
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
|
@ -317,9 +328,9 @@ class LiteDRAMBISTChecker(Module, AutoCSR):
|
||||||
core.length.eq(length_sync.o),
|
core.length.eq(length_sync.o),
|
||||||
]
|
]
|
||||||
|
|
||||||
error_count_sync = BusSynchronizer(32, cd, "sys")
|
err_count_sync = BusSynchronizer(32, cd, "sys")
|
||||||
self.submodules += error_count_sync
|
self.submodules += err_count_sync
|
||||||
self.comb += [
|
self.comb += [
|
||||||
error_count_sync.i.eq(core.error_count),
|
err_count_sync.i.eq(core.err_count),
|
||||||
self.error_count.status.eq(error_count_sync.o),
|
self.err_count.status.eq(err_count_sync.o),
|
||||||
]
|
]
|
||||||
|
|
|
@ -20,16 +20,6 @@ class TB(Module):
|
||||||
self.submodules.checker = LiteDRAMBISTChecker(self.read_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):
|
def main_generator(dut, mem):
|
||||||
# Populate memory with random data
|
# Populate memory with random data
|
||||||
random.seed(0)
|
random.seed(0)
|
||||||
|
@ -55,7 +45,7 @@ def main_generator(dut, mem):
|
||||||
|
|
||||||
# read with no errors
|
# read with no errors
|
||||||
yield from reset_bist_module(dut.checker)
|
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
|
assert errors == 0, errors
|
||||||
|
|
||||||
yield dut.checker.base.storage.eq(16)
|
yield dut.checker.base.storage.eq(16)
|
||||||
|
@ -69,7 +59,7 @@ def main_generator(dut, mem):
|
||||||
yield
|
yield
|
||||||
done = yield dut.checker.done.status
|
done = yield dut.checker.done.status
|
||||||
assert done, done
|
assert done, done
|
||||||
errors = yield dut.checker.error_count.status
|
errors = yield dut.checker.err_count.status
|
||||||
assert errors == 0, errors
|
assert errors == 0, errors
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
@ -77,7 +67,7 @@ def main_generator(dut, mem):
|
||||||
|
|
||||||
# read with one error
|
# read with one error
|
||||||
yield from reset_bist_module(dut.checker)
|
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
|
assert errors == 0, errors
|
||||||
|
|
||||||
print("mem.mem[20]", hex(mem.mem[20]))
|
print("mem.mem[20]", hex(mem.mem[20]))
|
||||||
|
@ -95,7 +85,7 @@ def main_generator(dut, mem):
|
||||||
yield
|
yield
|
||||||
done = yield dut.checker.done.status
|
done = yield dut.checker.done.status
|
||||||
assert done, done
|
assert done, done
|
||||||
errors = yield dut.checker.error_count.status
|
errors = yield dut.checker.err_count.status
|
||||||
assert errors == 1, errors
|
assert errors == 1, errors
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
@ -103,7 +93,7 @@ def main_generator(dut, mem):
|
||||||
|
|
||||||
# read with two errors
|
# read with two errors
|
||||||
yield from reset_bist_module(dut.checker)
|
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
|
assert errors == 0, errors
|
||||||
|
|
||||||
print("mem.mem[21]", hex(mem.mem[21]))
|
print("mem.mem[21]", hex(mem.mem[21]))
|
||||||
|
@ -121,7 +111,7 @@ def main_generator(dut, mem):
|
||||||
yield
|
yield
|
||||||
done = yield dut.checker.done.status
|
done = yield dut.checker.done.status
|
||||||
assert done, done
|
assert done, done
|
||||||
errors = yield dut.checker.error_count.status
|
errors = yield dut.checker.err_count.status
|
||||||
assert errors == 2, errors
|
assert errors == 2, errors
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
@ -129,7 +119,7 @@ def main_generator(dut, mem):
|
||||||
|
|
||||||
# read with two errors but halting on the first one
|
# read with two errors but halting on the first one
|
||||||
yield from reset_bist_module(dut.checker)
|
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
|
assert errors == 0, errors
|
||||||
|
|
||||||
yield dut.checker.base.storage.eq(16)
|
yield dut.checker.base.storage.eq(16)
|
||||||
|
@ -144,12 +134,12 @@ def main_generator(dut, mem):
|
||||||
|
|
||||||
err_addr = yield dut.checker.core._data_counter + dut.checker.core.base
|
err_addr = yield dut.checker.core._data_counter + dut.checker.core.base
|
||||||
assert err_addr == 20, err_addr
|
assert err_addr == 20, err_addr
|
||||||
err_expect = yield dut.checker.core.expected
|
err_expect = yield dut.checker.core.expect
|
||||||
assert err_expect == 0xffff000f, hex(err_expect)
|
assert err_expect == 0xffff000f, hex(err_expect)
|
||||||
err_actual = yield dut.checker.core.actual
|
err_actual = yield dut.checker.core.actual
|
||||||
assert err_actual == 0x200, err_actual
|
assert err_actual == 0x200, err_actual
|
||||||
yield
|
yield
|
||||||
errors = yield dut.checker.core.error_count
|
errors = yield dut.checker.core.err_count
|
||||||
assert errors == 1, errors
|
assert errors == 1, errors
|
||||||
|
|
||||||
while((yield dut.checker.core.error) == 0):
|
while((yield dut.checker.core.error) == 0):
|
||||||
|
@ -157,31 +147,25 @@ def main_generator(dut, mem):
|
||||||
|
|
||||||
err_addr = yield dut.checker.core._data_counter + dut.checker.core.base
|
err_addr = yield dut.checker.core._data_counter + dut.checker.core.base
|
||||||
assert err_addr == 21, err_addr
|
assert err_addr == 21, err_addr
|
||||||
err_expect = yield dut.checker.core.expected
|
err_expect = yield dut.checker.core.expect
|
||||||
assert err_expect == 0xfff1ff1f, hex(err_expect)
|
assert err_expect == 0xfff1ff1f, hex(err_expect)
|
||||||
err_actual = yield dut.checker.core.actual
|
err_actual = yield dut.checker.core.actual
|
||||||
assert err_actual == 0x210, hex(err_actual)
|
assert err_actual == 0x210, hex(err_actual)
|
||||||
yield
|
yield
|
||||||
errors = yield dut.checker.core.error_count
|
errors = yield dut.checker.core.err_count
|
||||||
assert errors == 2, errors
|
assert errors == 2, errors
|
||||||
|
|
||||||
while((yield dut.checker.done.status) == 0):
|
while((yield dut.checker.done.status) == 0):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
done = yield dut.checker.done.status
|
done = yield dut.checker.done.status
|
||||||
assert done, done
|
assert done, done
|
||||||
running = yield dut.checker.core.running
|
errors = yield dut.checker.err_count.status
|
||||||
assert running, running
|
|
||||||
for i in range(16):
|
|
||||||
yield
|
|
||||||
|
|
||||||
errors = yield dut.checker.error_count.status
|
|
||||||
assert errors == 2, errors
|
assert errors == 2, errors
|
||||||
|
|
||||||
yield
|
yield
|
||||||
yield
|
yield
|
||||||
|
|
||||||
finished.append(True)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
tb = TB()
|
tb = TB()
|
||||||
|
@ -191,7 +175,6 @@ if __name__ == "__main__":
|
||||||
main_generator(tb, mem),
|
main_generator(tb, mem),
|
||||||
mem.write_generator(tb.write_port),
|
mem.write_generator(tb.write_port),
|
||||||
mem.read_generator(tb.read_port),
|
mem.read_generator(tb.read_port),
|
||||||
cycle_assert(tb),
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
clocks = {"sys": 10}
|
clocks = {"sys": 10}
|
||||||
|
|
|
@ -19,9 +19,6 @@ def reset_bist_module(module):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
# Check some initial conditions are correct after reset.
|
# Check some initial conditions are correct after reset.
|
||||||
running = yield module.core.running
|
|
||||||
assert running == 0, running
|
|
||||||
|
|
||||||
done = yield module.done.status
|
done = yield module.done.status
|
||||||
assert not done, done
|
assert not done, done
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue