sim: memory access from generators

This commit is contained in:
Sebastien Bourdeauducq 2015-09-20 14:52:26 +08:00
parent 59802bec76
commit 320dffb4ac
4 changed files with 66 additions and 23 deletions

View 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()

View 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

View 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

View 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