sim: memory access from generators
authorSebastien Bourdeauducq <sb@m-labs.hk>
Sun, 20 Sep 2015 06:52:26 +0000 (14:52 +0800)
committerSebastien Bourdeauducq <sb@m-labs.hk>
Sun, 20 Sep 2015 06:52:26 +0000 (14:52 +0800)
examples/sim/memory.py
migen/fhdl/simplify.py
migen/fhdl/specials.py
migen/sim.py

index 7bd3d5fb04e39d65fb0f864c22d75988ccdee000..3071bc9ece727a237035421e2ce1821b9c3a67fb 100644 (file)
@@ -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()
index 004c89591afc410943d245f31c853d00d4428ed8..6d91fd0eba51188d38d0c9316663023d6f013b01 100644 (file)
@@ -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
index 4137d746a7deb4d4f9ab192260811284c4020892..873c86fbd9413e58efbd9d6b8053a04428a6b8f5 100644 (file)
@@ -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
 
 
index 0fabe2b2f35e814a3898553b724489e753d21f96..b34d442486673b07240b5493fb1644046a4ecc7b 100644 (file)
@@ -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