sim: memory access from generators
This commit is contained in:
parent
59802bec76
commit
320dffb4ac
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
22
migen/sim.py
22
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
|
||||
|
|
Loading…
Reference in New Issue