diff --git a/examples/sim/memory.py b/examples/sim/memory.py index 7bd3d5fb0..3071bc9ec 100644 --- a/examples/sim/memory.py +++ b/examples/sim/memory.py @@ -7,19 +7,20 @@ class Mem(Module): # from 0 to 19. self.specials.mem = Memory(16, 2**12, init=list(range(20))) - def do_simulation(self, selfp): - # Read the memory. Use the cycle counter as address. - value = selfp.mem[selfp.simulator.cycle_counter] - # Print the result. Output is: - # 0 - # 1 - # 2 - # ... + +def memory_test(dut): + # write (only first 5 values) + for i in range(5): + yield dut.mem[i], 42 + i + # remember: values are written after the tick, and read before the tick. + # wait one tick for the memory to update. + yield + # read what we have written, plus some initialization data + for i in range(10): + value = yield dut.mem[i] print(value) - # Raising StopSimulation disables the current (and here, only one) - # simulation function. Simulator stops when all functions are disabled. - if value == 10: - raise StopSimulation + if __name__ == "__main__": - run_simulation(Mem()) + dut = Mem() + Simulator(dut, memory_test(dut)).run() diff --git a/migen/fhdl/simplify.py b/migen/fhdl/simplify.py index 004c89591..6d91fd0eb 100644 --- a/migen/fhdl/simplify.py +++ b/migen/fhdl/simplify.py @@ -5,6 +5,9 @@ from migen.util.misc import gcd_multiple class FullMemoryWE(ModuleTransformer): + def __init__(self): + self.replacments = dict() + def transform_fragment(self, i, f): newspecials = set() @@ -16,6 +19,7 @@ class FullMemoryWE(ModuleTransformer): if global_granularity == orig.width: newspecials.add(orig) # nothing to do else: + newmems = [] for i in range(orig.width//global_granularity): if orig.init is None: newinit = None @@ -23,6 +27,7 @@ class FullMemoryWE(ModuleTransformer): newinit = [(v >> i*global_granularity) & (2**global_granularity - 1) for v in orig.init] newmem = Memory(global_granularity, orig.depth, newinit, orig.name_override + "_grain" + str(i)) newspecials.add(newmem) + newmems.append(newmem) for port in orig.ports: port_granularity = port.we_granularity if port.we_granularity else orig.width newport = _MemoryPort( @@ -39,11 +44,15 @@ class FullMemoryWE(ModuleTransformer): clock_domain=port.clock) newmem.ports.append(newport) newspecials.add(newport) + self.replacments[orig] = newmems f.specials = newspecials class MemoryToArray(ModuleTransformer): + def __init__(self): + self.replacements = dict() + def transform_fragment(self, i, f): newspecials = set() @@ -53,6 +62,7 @@ class MemoryToArray(ModuleTransformer): continue storage = Array() + self.replacements[mem] = storage init = [] if mem.init is not None: init = mem.init @@ -90,6 +100,15 @@ class MemoryToArray(ModuleTransformer): # write if port.we is not None: - sync.append(If(port.we, storage[port.adr].eq(port.dat_w))) + if port.we_granularity: + n = mem.width//port.we_granularity + for i in range(n): + m = i*port.we_granularity + M = (i+1)*port.we_granularity + sync.append(If(port.we[i], + storage[port.adr][m:M].eq(port.dat_w))) + else: + sync.append(If(port.we, + storage[port.adr].eq(port.dat_w))) f.specials = newspecials diff --git a/migen/fhdl/specials.py b/migen/fhdl/specials.py index 4137d746a..873c86fbd 100644 --- a/migen/fhdl/specials.py +++ b/migen/fhdl/specials.py @@ -1,7 +1,7 @@ from operator import itemgetter from migen.fhdl.structure import * -from migen.fhdl.structure import _DUID +from migen.fhdl.structure import _DUID, _Value from migen.fhdl.bitcontainer import bits_for, value_bits_sign from migen.fhdl.tools import * from migen.fhdl.tracer import get_obj_var_name @@ -210,6 +210,18 @@ class _MemoryPort(Special): return "" # done by parent Memory object +class _MemoryLocation(_Value): + def __init__(self, memory, index): + _Value.__init__(self) + if isinstance(index, (bool, int)): + index = Constant(index) + if not isinstance(index, _Value): + raise TypeError("Memory index is not a Migen value: {}" + .format(index)) + self.memory = memory + self.index = index + + class Memory(Special): def __init__(self, width, depth, init=None, name=None): Special.__init__(self) @@ -219,6 +231,10 @@ class Memory(Special): self.init = init self.name_override = get_obj_var_name(name, "mem") + def __getitem__(self, index): + # simulation only + return _MemoryLocation(self, index) + def get_port(self, write_capable=False, async_read=False, has_re=False, we_granularity=0, mode=WRITE_FIRST, clock_domain="sys"): @@ -325,7 +341,6 @@ class Memory(Special): r += "\t$readmemh(\"" + memory_filename + "\", " + gn(memory) + ");\n" r += "end\n\n" - return r diff --git a/migen/sim.py b/migen/sim.py index 0fabe2b2f..b34d44248 100644 --- a/migen/sim.py +++ b/migen/sim.py @@ -1,11 +1,12 @@ import operator from migen.fhdl.structure import * -from migen.fhdl.structure import (_Operator, _Slice, _ArrayProxy, +from migen.fhdl.structure import (_Value, _Operator, _Slice, _ArrayProxy, _Assign, _Fragment) from migen.fhdl.bitcontainer import flen from migen.fhdl.tools import list_targets -from migen.fhdl.simplify import FullMemoryWE, MemoryToArray +from migen.fhdl.simplify import MemoryToArray +from migen.fhdl.specials import _MemoryLocation __all__ = ["Simulator"] @@ -61,7 +62,8 @@ str2op = { class Evaluator: - def __init__(self): + def __init__(self, replaced_memories): + self.replaced_memories = replaced_memories self.signal_values = dict() self.modifications = dict() @@ -114,6 +116,9 @@ class Evaluator: elif isinstance(node, _ArrayProxy): return self.eval(node.choices[self.eval(node.key, postcommit)], postcommit) + elif isinstance(node, _MemoryLocation): + array = self.replaced_memories[node.memory] + return self.eval(array[self.eval(node.index, postcommit)], postcommit) else: # TODO: ClockSignal, ResetSignal raise NotImplementedError @@ -140,6 +145,9 @@ class Evaluator: self.assign(node, full_value) elif isinstance(node, _ArrayProxy): self.assign(node.choices[self.eval(node.key)], value) + elif isinstance(node, _MemoryLocation): + array = self.replaced_memories[node.memory] + self.assign(array[self.eval(node.index)], value) else: # TODO: ClockSignal, ResetSignal raise NotImplementedError @@ -182,15 +190,15 @@ class Simulator: else: self.generators[k] = [v] - FullMemoryWE().transform_fragment(None, self.fragment) - MemoryToArray().transform_fragment(None, self.fragment) + mta = MemoryToArray() + mta.transform_fragment(None, self.fragment) # TODO: insert_resets on sync # comb signals return to their reset value if nothing assigns them self.fragment.comb[0:0] = [s.eq(s.reset) for s in list_targets(self.fragment.comb)] self.time = TimeManager(clocks) - self.evaluator = Evaluator() + self.evaluator = Evaluator(mta.replacements) def _commit_and_comb_propagate(self): # TODO: optimize @@ -202,7 +210,7 @@ class Simulator: def _eval_nested_lists(self, x): if isinstance(x, list): return [self._eval_nested_lists(e) for e in x] - elif isinstance(x, Signal): + elif isinstance(x, _Value): return self.evaluator.eval(x) else: raise ValueError