Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/nmigen
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 240a40c2c2f8
Choose a base ref
...
head repository: m-labs/nmigen
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 1b7f8c795012
Choose a head ref
  • 2 commits
  • 1 file changed
  • 1 contributor

Commits on Dec 14, 2018

  1. Copy the full SHA
    105113f View commit details
  2. back.pysim: general clean-up.

    whitequark committed Dec 14, 2018
    Copy the full SHA
    1b7f8c7 View commit details
Showing with 80 additions and 67 deletions.
  1. +80 −67 nmigen/back/pysim.py
147 changes: 80 additions & 67 deletions nmigen/back/pysim.py
Original file line number Diff line number Diff line change
@@ -68,10 +68,10 @@ def on_Signal(self, value):
return lambda state: state.get(value)

def on_ClockSignal(self, value):
raise NotImplementedError
raise NotImplementedError # :nocov:

def on_ResetSignal(self, value):
raise NotImplementedError
raise NotImplementedError # :nocov:

def on_Operator(self, value):
shape = value.shape()
@@ -189,7 +189,7 @@ def run(state):


class Simulator:
def __init__(self, fragment=None, vcd_file=None, gtkw_file=None, gtkw_signals=()):
def __init__(self, fragment, vcd_file=None, gtkw_file=None, gtkw_signals=()):
self._fragments = {} # fragment -> hierarchy

self._domains = {} # str/domain -> ClockDomain
@@ -223,20 +223,20 @@ def __init__(self, fragment=None, vcd_file=None, gtkw_file=None, gtkw_signals=()
self._gtkw_file = gtkw_file
self._gtkw_signals = gtkw_signals

if fragment is not None:
fragment = fragment.prepare()
self._add_fragment(fragment)
self._domains = fragment.domains
for domain, cd in self._domains.items():
self._domain_triggers[cd.clk] = domain
if cd.rst is not None:
self._domain_triggers[cd.rst] = domain
self._domain_signals[domain] = ValueSet()
fragment = fragment.prepare()

def _add_fragment(self, fragment, hierarchy=("top",)):
self._fragments[fragment] = hierarchy
for subfragment, name in fragment.subfragments:
self._add_fragment(subfragment, (*hierarchy, name))
def add_fragment(fragment, hierarchy=("top",)):
self._fragments[fragment] = hierarchy
for subfragment, name in fragment.subfragments:
add_fragment(subfragment, (*hierarchy, name))
add_fragment(fragment)

self._domains = fragment.domains
for domain, cd in self._domains.items():
self._domain_triggers[cd.clk] = domain
if cd.rst is not None:
self._domain_triggers[cd.rst] = domain
self._domain_signals[domain] = ValueSet()

def add_process(self, process):
self._processes.add(process)
@@ -267,17 +267,6 @@ def sync_process():
pass
self.add_process(sync_process())

def _signal_name_in_fragment(self, fragment, signal):
for subfragment, name in fragment.subfragments:
if signal in subfragment.ports:
return "{}_{}".format(name, signal.name)
return signal.name

def _add_funclet(self, signal, funclet):
if signal not in self._funclets:
self._funclets[signal] = set()
self._funclets[signal].add(funclet)

def __enter__(self):
if self._vcd_file:
self._vcd_writer = VCDWriter(self._vcd_file, timescale="100 ps",
@@ -293,8 +282,14 @@ def __enter__(self):

if signal not in self._vcd_signals:
self._vcd_signals[signal] = set()
name = self._signal_name_in_fragment(fragment, signal)
suffix = None

for subfragment, name in fragment.subfragments:
if signal in subfragment.ports:
var_name = "{}_{}".format(name, signal.name)
break
else:
var_name = signal.name

if signal.decoder:
var_type = "string"
var_size = 1
@@ -303,18 +298,20 @@ def __enter__(self):
var_type = "wire"
var_size = signal.nbits
var_init = signal.reset

suffix = None
while True:
try:
if suffix is None:
name_suffix = name
var_name_suffix = var_name
else:
name_suffix = "{}${}".format(name, suffix)
var_name_suffix = "{}${}".format(var_name, suffix)
self._vcd_signals[signal].add(self._vcd_writer.register_var(
scope=".".join(self._fragments[fragment]), name=name_suffix,
scope=".".join(self._fragments[fragment]), name=var_name_suffix,
var_type=var_type, size=var_size, init=var_init))
if signal not in self._vcd_names:
self._vcd_names[signal] = \
".".join(self._fragments[fragment] + (name_suffix,))
".".join(self._fragments[fragment] + (var_name_suffix,))
break
except KeyError:
suffix = (suffix or 0) + 1
@@ -331,14 +328,19 @@ def __enter__(self):
statements.append(signal.eq(signal.reset))
statements += fragment.statements

def add_funclet(signal, funclet):
if signal not in self._funclets:
self._funclets[signal] = set()
self._funclets[signal].add(funclet)

compiler = _StatementCompiler()
funclet = compiler(statements)
for signal in compiler.sensitivity:
self._add_funclet(signal, funclet)
add_funclet(signal, funclet)
for domain, cd in fragment.domains.items():
self._add_funclet(cd.clk, funclet)
add_funclet(cd.clk, funclet)
if cd.rst is not None:
self._add_funclet(cd.rst, funclet)
add_funclet(cd.rst, funclet)

self._user_signals = self._signals - self._comb_signals - self._sync_signals

@@ -406,46 +408,57 @@ def _commit_sync_signals(self, domains):
self._commit_signal(signal, domains)

# Wake up any simulator processes that wait for a domain tick.
for proc, wait_domain in list(self._wait_tick.items()):
for process, wait_domain in list(self._wait_tick.items()):
if domain == wait_domain:
del self._wait_tick[proc]
self._suspended.remove(proc)
del self._wait_tick[process]
self._suspended.remove(process)

# Unless handling synchronous logic above has triggered more synchronous logic (which
# can happen e.g. if a domain is clocked off a clock divisor in fabric), we're done.
# Otherwise, do one more round of updates.

def _force_signal(self, signal, value):
assert signal in self._user_signals
self._state.set_next(signal, value)

domains = set()
self._commit_signal(signal, domains)
self._commit_sync_signals(domains)

def _run_process(self, proc):
def _run_process(self, process):
try:
stmt = proc.send(None)
stmt = process.send(None)
except StopIteration:
self._processes.remove(proc)
self._passive.discard(proc)
self._processes.remove(process)
self._passive.discard(process)
return

if isinstance(stmt, Delay):
self._wait_deadline[proc] = self._timestamp + stmt.interval
self._suspended.add(proc)
self._wait_deadline[process] = self._timestamp + stmt.interval
self._suspended.add(process)

elif isinstance(stmt, Tick):
self._wait_tick[proc] = stmt.domain
self._suspended.add(proc)
self._wait_tick[process] = stmt.domain
self._suspended.add(process)

elif isinstance(stmt, Passive):
self._passive.add(proc)
self._passive.add(process)

elif isinstance(stmt, Assign):
assert isinstance(stmt.lhs, Signal)
assert isinstance(stmt.rhs, Const)
self._force_signal(stmt.lhs, normalize(stmt.rhs.value, stmt.lhs.shape()))
lhs_signals = stmt.lhs._lhs_signals()
for signal in lhs_signals:
if not signal in self._signals:
raise ValueError("Process {!r} sent a request to set signal '{!r}', "
"which is not a part of simulation"
.format(process, signal))
if signal in self._comb_signals:
raise ValueError("Process {!r} sent a request to set signal '{!r}', "
"which is a part of combinatorial assignment in simulation"
.format(process, signal))

funclet = _StatementCompiler()(stmt)
funclet(self._state)

domains = set()
for signal in lhs_signals:
self._commit_signal(signal, domains)
self._commit_sync_signals(domains)

else:
raise TypeError("Received unsupported statement '{!r}' from process {}"
.format(stmt, proc))
raise TypeError("Received unsupported statement '{!r}' from process {!r}"
.format(stmt, process))

def step(self, run_passive=False):
deadline = None
@@ -470,20 +483,20 @@ def step(self, run_passive=False):
# Are there any processes that haven't had a chance to run yet?
if len(self._processes) > len(self._suspended):
# Schedule an arbitrary one.
proc = (self._processes - set(self._suspended)).pop()
self._run_process(proc)
process = (self._processes - set(self._suspended)).pop()
self._run_process(process)
return True

# All processes are suspended. Are any of them active?
if len(self._processes) > len(self._passive) or run_passive:
# Are any of them suspended before a deadline?
if self._wait_deadline:
# Schedule the one with the lowest deadline.
proc, deadline = min(self._wait_deadline.items(), key=lambda x: x[1])
del self._wait_deadline[proc]
self._suspended.remove(proc)
process, deadline = min(self._wait_deadline.items(), key=lambda x: x[1])
del self._wait_deadline[process]
self._suspended.remove(process)
self._timestamp = deadline
self._run_process(proc)
self._run_process(process)
return True

# No processes, or all processes are passive. Nothing to do!