From fcbcd4d3fea76431f65751ea52045a46faaebe40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Tue, 4 Feb 2020 12:34:15 +0100 Subject: [PATCH] test: add option to benchmark predefined access patterns --- litedram/frontend/bist.py | 136 ++++++++++++++++++++++++++++++++++++++ test/benchmark.py | 74 ++++++++++++++------- 2 files changed, 186 insertions(+), 24 deletions(-) diff --git a/litedram/frontend/bist.py b/litedram/frontend/bist.py index 16dd7d3..1506a97 100644 --- a/litedram/frontend/bist.py +++ b/litedram/frontend/bist.py @@ -185,6 +185,60 @@ class _LiteDRAMBISTGenerator(Module): raise NotImplementedError self.comb += dma.sink.data.eq(data_gen.o) +@ResetInserter() +class _LiteDRAMPatternGenerator(Module): + def __init__(self, dram_port, init=[]): + ashift, awidth = get_ashift_awidth(dram_port) + self.start = Signal() + self.done = Signal() + self.ticks = Signal(32) + + # # # + + # DMA -------------------------------------------------------------------------------------- + dma = LiteDRAMDMAWriter(dram_port) + self.submodules += dma + + cmd_counter = Signal(dram_port.address_width, reset_less=True) + + # Data / Address FSM ----------------------------------------------------------------------- + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + fsm.act("IDLE", + If(self.start, + NextValue(cmd_counter, 0), + NextState("RUN") + ), + NextValue(self.ticks, 0) + ) + fsm.act("RUN", + dma.sink.valid.eq(1), + If(dma.sink.ready, + NextValue(cmd_counter, cmd_counter + 1), + If(cmd_counter == (len(init) - 1), + NextState("DONE") + ) + ), + NextValue(self.ticks, self.ticks + 1) + ) + fsm.act("DONE", + self.done.eq(1) + ) + + if isinstance(dram_port, LiteDRAMNativePort): # addressing in dwords + dma_sink_addr = dma.sink.address + elif isinstance(dram_port, LiteDRAMAXIPort): # addressing in bytes + dma_sink_addr = dma.sink.address[ashift:] + else: + raise NotImplementedError + + addr_cases = {i: dma_sink_addr.eq(addr) for i, (addr, data) in enumerate(init)} + data_cases = {i: dma.sink.data.eq(data) for i, (addr, data) in enumerate(init)} + self.comb += [ + Case(cmd_counter, addr_cases), + Case(cmd_counter, data_cases), + ] + # LiteDRAMBISTGenerator ---------------------------------------------------------------------------- class LiteDRAMBISTGenerator(Module, AutoCSR): @@ -367,6 +421,88 @@ class _LiteDRAMBISTChecker(Module, AutoCSR): self.done.eq(1) ) +@ResetInserter() +class _LiteDRAMPatternChecker(Module, AutoCSR): + def __init__(self, dram_port, init=[]): + ashift, awidth = get_ashift_awidth(dram_port) + self.start = Signal() + self.done = Signal() + self.ticks = Signal(32) + self.errors = Signal(32) + + # # # + + # DMA -------------------------------------------------------------------------------------- + dma = LiteDRAMDMAReader(dram_port) + self.submodules += dma + + # Address FSM ------------------------------------------------------------------------------ + cmd_counter = Signal(dram_port.address_width, reset_less=True) + + cmd_fsm = FSM(reset_state="IDLE") + self.submodules += cmd_fsm + cmd_fsm.act("IDLE", + If(self.start, + NextValue(cmd_counter, 0), + NextState("RUN") + ) + ) + cmd_fsm.act("RUN", + dma.sink.valid.eq(1), + If(dma.sink.ready, + NextValue(cmd_counter, cmd_counter + 1), + If(cmd_counter == (len(init) - 1), + NextState("DONE") + ) + ) + ) + cmd_fsm.act("DONE") + + if isinstance(dram_port, LiteDRAMNativePort): # addressing in dwords + dma_addr_sink = dma.sink.address + elif isinstance(dram_port, LiteDRAMAXIPort): # addressing in bytes + dma_addr_sink = dma.sink.address[ashift:] + else: + raise NotImplementedError + + addr_cases = {i: dma_addr_sink.eq(addr) for i, (addr, data) in enumerate(init)} + self.comb += Case(cmd_counter, addr_cases) + + # Data FSM --------------------------------------------------------------------------------- + data_counter = Signal(dram_port.address_width, reset_less=True) + + expected_data = Signal.like(dma.source.data) + data_cases = {i: expected_data.eq(data) for i, (addr, data) in enumerate(init)} + self.comb += Case(data_counter, data_cases) + + data_fsm = FSM(reset_state="IDLE") + self.submodules += data_fsm + data_fsm.act("IDLE", + If(self.start, + NextValue(data_counter, 0), + NextValue(self.errors, 0), + NextState("RUN") + ), + NextValue(self.ticks, 0) + ) + + data_fsm.act("RUN", + dma.source.ready.eq(1), + If(dma.source.valid, + NextValue(data_counter, data_counter + 1), + If(dma.source.data != expected_data, + NextValue(self.errors, self.errors + 1) + ), + If(data_counter == (len(init) - 1), + NextState("DONE") + ) + ), + NextValue(self.ticks, self.ticks + 1) + ) + data_fsm.act("DONE", + self.done.eq(1) + ) + # LiteDRAMBISTChecker ------------------------------------------------------------------------------ class LiteDRAMBISTChecker(Module, AutoCSR): diff --git a/test/benchmark.py b/test/benchmark.py index 61c55fa..9681c8a 100755 --- a/test/benchmark.py +++ b/test/benchmark.py @@ -3,6 +3,7 @@ # This file is Copyright (c) 2020 Florent Kermarrec # License: BSD +import csv import argparse from migen import * @@ -16,9 +17,8 @@ from litex.soc.integration.builder import * from litex.tools.litex_sim import SimSoC -from litedram.frontend.bist import _LiteDRAMBISTGenerator -from litedram.frontend.bist import _LiteDRAMBISTChecker - +from litedram.frontend.bist import _LiteDRAMBISTGenerator, _LiteDRAMBISTChecker, \ + _LiteDRAMPatternGenerator, _LiteDRAMPatternChecker # LiteDRAM Benchmark SoC --------------------------------------------------------------------------- @@ -29,6 +29,7 @@ class LiteDRAMBenchmarkSoC(SimSoC): bist_base = 0x00000000, bist_length = 1024, bist_random = False, + pattern_init = None, **kwargs): # SimSoC ----------------------------------------------------------------------------------- @@ -42,12 +43,34 @@ class LiteDRAMBenchmarkSoC(SimSoC): # make sure that we perform at least one access bist_length = max(bist_length, self.sdram.controller.interface.data_width // 8) - # BIST Generator --------------------------------------------------------------------------- - bist_generator = _LiteDRAMBISTGenerator(self.sdram.crossbar.get_port()) - self.submodules.bist_generator = bist_generator + # BIST Generator / Checker ----------------------------------------------------------------- + if pattern_init is None: + bist_generator = _LiteDRAMBISTGenerator(self.sdram.crossbar.get_port()) + bist_checker = _LiteDRAMBISTChecker(self.sdram.crossbar.get_port()) - # BIST Checker ----------------------------------------------------------------------------- - bist_checker = _LiteDRAMBISTChecker(self.sdram.crossbar.get_port()) + generator_config = [ + bist_generator.base.eq(bist_base), + bist_generator.length.eq(bist_length), + bist_generator.random.eq(bist_random), + ] + checker_config = [ + bist_checker.base.eq(bist_base), + bist_checker.length.eq(bist_length), + bist_checker.random.eq(bist_random), + ] + else: + # TODO: run checker in parallel to avoid overwriting previously written data + address_set = set() + for addr, _ in pattern_init: + assert addr not in address_set, \ + 'Duplicate address 0x%08x in pattern_init, write will overwrite previous value!' % addr + address_set.add(addr) + + bist_generator = _LiteDRAMPatternGenerator(self.sdram.crossbar.get_port(), init=pattern_init) + bist_checker = _LiteDRAMPatternChecker(self.sdram.crossbar.get_port(), init=pattern_init) + generator_config = checker_config = [] + + self.submodules.bist_generator = bist_generator self.submodules.bist_checker = bist_checker # Sequencer -------------------------------------------------------------------------------- @@ -68,18 +91,14 @@ class LiteDRAMBenchmarkSoC(SimSoC): ) fsm.act("BIST-GENERATOR", bist_generator.start.eq(1), - bist_generator.base.eq(bist_base), - bist_generator.length.eq(bist_length), - bist_generator.random.eq(bist_random), + *generator_config, If(bist_generator.done, NextState("BIST-CHECKER") ) ) fsm.act("BIST-CHECKER", bist_checker.start.eq(1), - bist_checker.base.eq(bist_base), - bist_checker.length.eq(bist_length), - bist_checker.random.eq(bist_random), + *checker_config, If(bist_checker.done, NextState("DISPLAY") ) @@ -113,16 +132,17 @@ def main(): parser = argparse.ArgumentParser(description="LiteDRAM Benchmark SoC Simulation") builder_args(parser) soc_sdram_args(parser) - parser.add_argument("--threads", default=1, help="Set number of threads (default=1)") - parser.add_argument("--sdram-module", default="MT48LC16M16", help="Select SDRAM chip") - parser.add_argument("--sdram-data-width", default=32, help="Set SDRAM chip data width") - parser.add_argument("--trace", action="store_true", help="Enable VCD tracing") - parser.add_argument("--trace-start", default=0, help="Cycle to start VCD tracing") - parser.add_argument("--trace-end", default=-1, help="Cycle to end VCD tracing") - parser.add_argument("--opt-level", default="O0", help="Compilation optimization level") - parser.add_argument("--bist-base", default="0x00000000", help="Base address of the test (default=0)") - parser.add_argument("--bist-length", default="1024", help="Length of the test (default=1024)") - parser.add_argument("--bist-random", action="store_true", help="Use random data during the test") + parser.add_argument("--threads", default=1, help="Set number of threads (default=1)") + parser.add_argument("--sdram-module", default="MT48LC16M16", help="Select SDRAM chip") + parser.add_argument("--sdram-data-width", default=32, help="Set SDRAM chip data width") + parser.add_argument("--trace", action="store_true", help="Enable VCD tracing") + parser.add_argument("--trace-start", default=0, help="Cycle to start VCD tracing") + parser.add_argument("--trace-end", default=-1, help="Cycle to end VCD tracing") + parser.add_argument("--opt-level", default="O0", help="Compilation optimization level") + parser.add_argument("--bist-base", default="0x00000000", help="Base address of the test (default=0)") + parser.add_argument("--bist-length", default="1024", help="Length of the test (default=1024)") + parser.add_argument("--bist-random", action="store_true", help="Use random data during the test") + parser.add_argument("--access-pattern", help="Load access pattern (address, data) from CSV (ignores --bist-*)") args = parser.parse_args() soc_kwargs = soc_sdram_argdict(args) @@ -138,6 +158,12 @@ def main(): soc_kwargs["bist_length"] = int(args.bist_length, 0) soc_kwargs["bist_random"] = args.bist_random + if args.access_pattern: + with open(args.access_pattern, newline='') as f: + reader = csv.reader(f) + pattern_init = [(int(addr, 0), int(data, 0)) for addr, data in reader] + soc_kwargs["pattern_init"] = pattern_init + # SoC ------------------------------------------------------------------------------------------ soc = LiteDRAMBenchmarkSoC(**soc_kwargs)