262 lines
11 KiB
Python
Executable File
262 lines
11 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# This file is Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
|
|
# License: BSD
|
|
|
|
import csv
|
|
import argparse
|
|
from operator import and_
|
|
from functools import reduce
|
|
from itertools import zip_longest
|
|
|
|
from migen import *
|
|
from migen.genlib.misc import WaitTimer
|
|
|
|
from litex.build.sim.config import SimConfig
|
|
|
|
from litex.soc.interconnect.csr import *
|
|
from litex.soc.integration.soc_sdram import *
|
|
from litex.soc.integration.builder import *
|
|
|
|
from litex.tools.litex_sim import SimSoC
|
|
|
|
from litedram.frontend.bist import _LiteDRAMBISTGenerator, _LiteDRAMBISTChecker, \
|
|
_LiteDRAMPatternGenerator, _LiteDRAMPatternChecker
|
|
|
|
# LiteDRAM Benchmark SoC ---------------------------------------------------------------------------
|
|
|
|
class LiteDRAMBenchmarkSoC(SimSoC):
|
|
def __init__(self,
|
|
sdram_module = "MT48LC16M16",
|
|
sdram_data_width = 32,
|
|
bist_base = 0x0000000,
|
|
bist_end = 0x0100000,
|
|
bist_length = 1024,
|
|
bist_random = False,
|
|
bist_alternating = False,
|
|
num_generators = 1,
|
|
num_checkers = 1,
|
|
pattern_init = None,
|
|
**kwargs):
|
|
|
|
# SimSoC -----------------------------------------------------------------------------------
|
|
SimSoC.__init__(self,
|
|
with_sdram = True,
|
|
sdram_module = sdram_module,
|
|
sdram_data_width = sdram_data_width,
|
|
**kwargs
|
|
)
|
|
|
|
# BIST Generator / Checker -----------------------------------------------------------------
|
|
|
|
# make sure that we perform at least one access
|
|
bist_length = max(bist_length, self.sdram.controller.interface.data_width // 8)
|
|
|
|
custom_pattern_mode = pattern_init is not None
|
|
|
|
if custom_pattern_mode:
|
|
make_generator = lambda: _LiteDRAMPatternGenerator(self.sdram.crossbar.get_port(), init=pattern_init)
|
|
make_checker = lambda: _LiteDRAMPatternChecker(self.sdram.crossbar.get_port(), init=pattern_init)
|
|
else:
|
|
make_generator = lambda: _LiteDRAMBISTGenerator(self.sdram.crossbar.get_port())
|
|
make_checker = lambda: _LiteDRAMBISTChecker(self.sdram.crossbar.get_port())
|
|
|
|
generators = [make_generator() for _ in range(num_generators)]
|
|
checkers = [make_checker() for _ in range(num_checkers)]
|
|
self.submodules += generators + checkers
|
|
|
|
if custom_pattern_mode:
|
|
def bist_config(module):
|
|
return []
|
|
|
|
if not bist_alternating:
|
|
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)
|
|
else:
|
|
def bist_config(module):
|
|
return [
|
|
module.base.eq(bist_base),
|
|
module.end.eq(bist_end),
|
|
module.length.eq(bist_length),
|
|
module.random_addr.eq(bist_random),
|
|
]
|
|
|
|
assert not (bist_random and not bist_alternating), \
|
|
'Write to random address may overwrite previously written data before reading!'
|
|
|
|
# check address correctness
|
|
assert bist_end > bist_base
|
|
assert bist_end <= 2**(len(generators[0].end)) - 1, 'End address outside of range'
|
|
bist_addr_range = bist_end - bist_base
|
|
assert bist_addr_range > 0 and bist_addr_range & (bist_addr_range - 1) == 0, \
|
|
'Length of the address range must be a power of 2'
|
|
|
|
def combined_read(modules, signal, operator):
|
|
sig = Signal()
|
|
self.comb += sig.eq(reduce(operator, (getattr(m, signal) for m in modules)))
|
|
return sig
|
|
|
|
def combined_write(modules, signal):
|
|
sig = Signal()
|
|
self.comb += [getattr(m, signal).eq(sig) for m in modules]
|
|
return sig
|
|
|
|
# Sequencer --------------------------------------------------------------------------------
|
|
class LiteDRAMCoreControl(Module, AutoCSR):
|
|
def __init__(self):
|
|
self.init_done = CSRStorage()
|
|
self.init_error = CSRStorage()
|
|
self.submodules.ddrctrl = ddrctrl = LiteDRAMCoreControl()
|
|
self.add_csr("ddrctrl")
|
|
|
|
display = Signal()
|
|
finish = Signal()
|
|
self.submodules.fsm = fsm = FSM(reset_state="WAIT-INIT")
|
|
fsm.act("WAIT-INIT",
|
|
If(self.ddrctrl.init_done.storage, # Written by CPU when initialization is done
|
|
NextState("BIST-GENERATOR")
|
|
)
|
|
)
|
|
if bist_alternating:
|
|
# force generators to wait for checkers and vice versa
|
|
# connect them in pairs, with each unpaired connected to the first of the others
|
|
bist_connections = []
|
|
for generator, checker in zip_longest(generators, checkers):
|
|
g = generator or generators[0]
|
|
c = checker or checkers[0]
|
|
bist_connections += g.run.eq(c.ready), c.run.eq(g.ready)
|
|
|
|
fsm.act("BIST-GENERATOR",
|
|
combined_write(generators + checkers, 'start').eq(1),
|
|
*bist_connections,
|
|
*map(bist_config, generators + checkers),
|
|
If(combined_read(checkers, 'done', and_),
|
|
NextState("DISPLAY")
|
|
)
|
|
)
|
|
else:
|
|
fsm.act("BIST-GENERATOR",
|
|
combined_write(generators, 'start').eq(1),
|
|
combined_write(generators, 'run').eq(1),
|
|
*map(bist_config, generators),
|
|
If(combined_read(generators, 'done', and_),
|
|
NextState("BIST-CHECKER")
|
|
)
|
|
)
|
|
fsm.act("BIST-CHECKER",
|
|
combined_write(checkers, 'start').eq(1),
|
|
combined_write(checkers, 'run').eq(1),
|
|
*map(bist_config, checkers),
|
|
If(combined_read(checkers, 'done', and_),
|
|
NextState("DISPLAY")
|
|
)
|
|
)
|
|
fsm.act("DISPLAY",
|
|
display.eq(1),
|
|
NextState("FINISH")
|
|
)
|
|
fsm.act("FINISH",
|
|
finish.eq(1)
|
|
)
|
|
|
|
# Simulation Results -----------------------------------------------------------------------
|
|
def max_signal(signals):
|
|
signals = iter(signals)
|
|
s = next(signals)
|
|
out = Signal(len(s))
|
|
self.comb += out.eq(s)
|
|
for curr in signals:
|
|
prev = out
|
|
out = Signal(max(len(prev), len(curr)))
|
|
self.comb += If(prev > curr, out.eq(prev)).Else(out.eq(curr))
|
|
return out
|
|
|
|
generator_ticks = max_signal((g.ticks for g in generators))
|
|
checker_errors = max_signal((c.errors for c in checkers))
|
|
checker_ticks = max_signal((c.ticks for c in checkers))
|
|
|
|
self.sync += [
|
|
If(display,
|
|
Display("BIST-GENERATOR ticks: %08d", generator_ticks),
|
|
Display("BIST-CHECKER errors: %08d", checker_errors),
|
|
Display("BIST-CHECKER ticks: %08d", checker_ticks),
|
|
)
|
|
]
|
|
|
|
# Simulation End ---------------------------------------------------------------------------
|
|
end_timer = WaitTimer(2**16)
|
|
self.submodules += end_timer
|
|
self.comb += end_timer.wait.eq(finish)
|
|
self.sync += If(end_timer.done, Finish())
|
|
|
|
# Build --------------------------------------------------------------------------------------------
|
|
|
|
def load_access_pattern(filename):
|
|
with open(filename, newline='') as f:
|
|
reader = csv.reader(f)
|
|
pattern_init = [(int(addr, 0), int(data, 0)) for addr, data in reader]
|
|
return pattern_init
|
|
|
|
|
|
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("--sdram-verbosity", default=0, help="Set SDRAM checker verbosity")
|
|
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("--bist-alternating", action="store_true", help="Perform alternating writes/reads (WRWRWR... instead of WWW...RRR...)")
|
|
parser.add_argument("--num-generators", default=1, help="Number of BIST generators")
|
|
parser.add_argument("--num-checkers", default=1, help="Number of BIST checkers")
|
|
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)
|
|
builder_kwargs = builder_argdict(args)
|
|
|
|
sim_config = SimConfig(default_clk="sys_clk")
|
|
sim_config.add_module("serial2console", "serial")
|
|
|
|
# Configuration --------------------------------------------------------------------------------
|
|
soc_kwargs["sdram_module"] = args.sdram_module
|
|
soc_kwargs["sdram_data_width"] = int(args.sdram_data_width)
|
|
soc_kwargs["sdram_verbosity"] = int(args.sdram_verbosity)
|
|
soc_kwargs["bist_base"] = int(args.bist_base, 0)
|
|
soc_kwargs["bist_length"] = int(args.bist_length, 0)
|
|
soc_kwargs["bist_random"] = args.bist_random
|
|
soc_kwargs["bist_alternating"] = args.bist_alternating
|
|
soc_kwargs["num_generators"] = int(args.num_generators)
|
|
soc_kwargs["num_checkers"] = int(args.num_checkers)
|
|
|
|
if args.access_pattern:
|
|
soc_kwargs["pattern_init"] = load_access_pattern(args.access_pattern)
|
|
|
|
# SoC ------------------------------------------------------------------------------------------
|
|
soc = LiteDRAMBenchmarkSoC(**soc_kwargs)
|
|
|
|
# Build/Run ------------------------------------------------------------------------------------
|
|
builder_kwargs["csr_csv"] = "csr.csv"
|
|
builder = Builder(soc, **builder_kwargs)
|
|
vns = builder.build(
|
|
threads = args.threads,
|
|
sim_config = sim_config,
|
|
opt_level = args.opt_level,
|
|
trace = args.trace,
|
|
trace_start = int(args.trace_start),
|
|
trace_end = int(args.trace_end)
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|