2013-06-25 16:17:39 -04:00
|
|
|
from collections import OrderedDict
|
|
|
|
|
2013-05-22 11:11:09 -04:00
|
|
|
from migen.fhdl.std import *
|
2013-06-25 16:17:39 -04:00
|
|
|
from migen.fhdl.module import FinalizeError
|
|
|
|
from migen.fhdl.visit import NodeTransformer
|
2012-01-09 10:28:48 -05:00
|
|
|
|
2013-06-25 16:17:39 -04:00
|
|
|
class AnonymousState:
|
|
|
|
pass
|
|
|
|
|
|
|
|
# do not use namedtuple here as it inherits tuple
|
|
|
|
# and the latter is used elsewhere in FHDL
|
|
|
|
class NextState:
|
|
|
|
def __init__(self, state):
|
|
|
|
self.state = state
|
|
|
|
|
|
|
|
class _LowerNextState(NodeTransformer):
|
|
|
|
def __init__(self, next_state_signal, encoding, aliases):
|
|
|
|
self.next_state_signal = next_state_signal
|
|
|
|
self.encoding = encoding
|
|
|
|
self.aliases = aliases
|
2012-03-17 19:09:40 -04:00
|
|
|
|
2013-06-25 16:17:39 -04:00
|
|
|
def visit_unknown(self, node):
|
|
|
|
if isinstance(node, NextState):
|
|
|
|
try:
|
|
|
|
actual_state = self.aliases[node.state]
|
|
|
|
except KeyError:
|
|
|
|
actual_state = node.state
|
|
|
|
return self.next_state_signal.eq(self.encoding[actual_state])
|
|
|
|
else:
|
|
|
|
return node
|
|
|
|
|
|
|
|
class FSM(Module):
|
2013-07-22 04:09:12 -04:00
|
|
|
def __init__(self, reset_state=None):
|
2013-06-25 16:17:39 -04:00
|
|
|
self.actions = OrderedDict()
|
|
|
|
self.state_aliases = dict()
|
2013-07-22 04:09:12 -04:00
|
|
|
self.reset_state = reset_state
|
2013-06-25 16:17:39 -04:00
|
|
|
|
2013-11-21 14:44:01 -05:00
|
|
|
self.before_entering_signals = OrderedDict()
|
|
|
|
self.before_leaving_signals = OrderedDict()
|
|
|
|
self.after_entering_signals = OrderedDict()
|
|
|
|
self.after_leaving_signals = OrderedDict()
|
2013-09-06 08:51:18 -04:00
|
|
|
|
2012-01-09 10:28:48 -05:00
|
|
|
def act(self, state, *statements):
|
2013-06-25 16:17:39 -04:00
|
|
|
if self.finalized:
|
|
|
|
raise FinalizeError
|
|
|
|
if state not in self.actions:
|
|
|
|
self.actions[state] = []
|
2012-11-28 17:18:43 -05:00
|
|
|
self.actions[state] += statements
|
2013-06-25 16:17:39 -04:00
|
|
|
|
|
|
|
def delayed_enter(self, name, target, delay):
|
|
|
|
if self.finalized:
|
|
|
|
raise FinalizeError
|
|
|
|
if delay:
|
|
|
|
state = name
|
|
|
|
for i in range(delay):
|
|
|
|
if i == delay - 1:
|
|
|
|
next_state = target
|
|
|
|
else:
|
|
|
|
next_state = AnonymousState()
|
|
|
|
self.act(state, NextState(next_state))
|
|
|
|
state = next_state
|
|
|
|
else:
|
|
|
|
self.state_aliases[name] = target
|
2013-09-06 08:51:18 -04:00
|
|
|
|
|
|
|
def ongoing(self, state):
|
|
|
|
is_ongoing = Signal()
|
|
|
|
self.act(state, is_ongoing.eq(1))
|
|
|
|
return is_ongoing
|
|
|
|
|
2013-11-21 14:44:01 -05:00
|
|
|
def _get_signal(self, d, state):
|
2013-09-06 08:51:18 -04:00
|
|
|
if state not in self.actions:
|
|
|
|
self.actions[state] = []
|
|
|
|
try:
|
|
|
|
return d[state]
|
|
|
|
except KeyError:
|
|
|
|
is_el = Signal()
|
|
|
|
d[state] = is_el
|
|
|
|
return is_el
|
|
|
|
|
2013-11-21 14:44:01 -05:00
|
|
|
def before_entering(self, state):
|
|
|
|
return self._get_signal(self.before_entering_signals, state)
|
|
|
|
|
|
|
|
def before_leaving(self, state):
|
|
|
|
return self._get_signal(self.before_leaving_signals, state)
|
|
|
|
|
|
|
|
def after_entering(self, state):
|
|
|
|
signal = self._get_signal(self.after_entering_signals, state)
|
|
|
|
self.sync += signal.eq(self.before_entering(state))
|
|
|
|
return signal
|
|
|
|
|
|
|
|
def after_leaving(self, state):
|
|
|
|
signal = self._get_signal(self.after_leaving_signals, state)
|
|
|
|
self.sync += signal.eq(self.before_leaving(state))
|
|
|
|
return signal
|
2013-09-06 08:51:18 -04:00
|
|
|
|
2013-06-25 16:17:39 -04:00
|
|
|
def do_finalize(self):
|
|
|
|
nstates = len(self.actions)
|
2013-09-06 08:51:18 -04:00
|
|
|
if self.reset_state is None:
|
|
|
|
reset_state = next(iter(self.actions.keys()))
|
|
|
|
else:
|
|
|
|
reset_state = self.reset_state
|
2013-06-25 16:17:39 -04:00
|
|
|
|
|
|
|
self.encoding = dict((s, n) for n, s in enumerate(self.actions.keys()))
|
2013-09-06 08:51:18 -04:00
|
|
|
self.state = Signal(max=nstates, reset=self.encoding[reset_state])
|
2013-06-25 16:17:39 -04:00
|
|
|
self.next_state = Signal(max=nstates)
|
|
|
|
|
|
|
|
lns = _LowerNextState(self.next_state, self.encoding, self.state_aliases)
|
|
|
|
cases = dict((self.encoding[k], lns.visit(v)) for k, v in self.actions.items() if v)
|
|
|
|
self.comb += [
|
|
|
|
self.next_state.eq(self.state),
|
2014-05-24 07:35:02 -04:00
|
|
|
Case(self.state, cases).makedefault(self.encoding[reset_state])
|
2012-01-09 10:28:48 -05:00
|
|
|
]
|
2013-06-25 16:17:39 -04:00
|
|
|
self.sync += self.state.eq(self.next_state)
|
2013-09-06 08:51:18 -04:00
|
|
|
|
|
|
|
# drive entering/leaving signals
|
2013-11-21 14:44:01 -05:00
|
|
|
for state, signal in self.before_leaving_signals.items():
|
2013-09-06 08:51:18 -04:00
|
|
|
encoded = self.encoding[state]
|
2013-11-21 14:44:01 -05:00
|
|
|
self.comb += signal.eq((self.state == encoded) & ~(self.next_state == encoded))
|
|
|
|
if reset_state in self.after_entering_signals:
|
|
|
|
self.after_entering_signals[reset_state].reset = 1
|
|
|
|
for state, signal in self.before_entering_signals.items():
|
2013-09-06 08:51:18 -04:00
|
|
|
encoded = self.encoding[state]
|
2013-11-21 14:44:01 -05:00
|
|
|
self.comb += signal.eq(~(self.state == encoded) & (self.next_state == encoded))
|