compat.genlib.fsm: import/wrap Migen code.
This commit is contained in:
		
							parent
							
								
									9661e897e6
								
							
						
					
					
						commit
						6251c95d4e
					
				|  | @ -30,7 +30,8 @@ proc_clean | |||
| write_verilog | ||||
| # Make sure there are no undriven wires in generated RTLIL. | ||||
| proc | ||||
| select -assert-none w:* i:* %a %d c:* %co* %a %d n:$* %d | ||||
| write_ilang x.il | ||||
| select -assert-none w:* i:* %a %d o:* %a %ci* %d c:* %co* %a %d n:$* %d | ||||
| """.format(il_text)) | ||||
|     if popen.returncode: | ||||
|         raise YosysError(error.strip()) | ||||
|  |  | |||
|  | @ -8,4 +8,4 @@ from .fhdl.bitcontainer import * | |||
| # from .sim import * | ||||
| 
 | ||||
| # from .genlib.record import * | ||||
| # from .genlib.fsm import * | ||||
| from .genlib.fsm import * | ||||
|  |  | |||
|  | @ -45,17 +45,41 @@ class If(ast.Switch): | |||
| 
 | ||||
| 
 | ||||
| class Case(ast.Switch): | ||||
|     @deprecated("instead of `Case(test, ...)`, use `with m.Case(test, ...):`") | ||||
|     @deprecated("instead of `Case(test, { value: stmts })`, use `with m.Switch(test):` and " | ||||
|                 "`with m.Case(value): stmts`; instead of `\"default\": stmts`, use " | ||||
|                 "`with m.Case(): stmts`") | ||||
|     def __init__(self, test, cases): | ||||
|         new_cases = [] | ||||
|         for k, v in cases.items(): | ||||
|             if k == "default": | ||||
|             if isinstance(k, (bool, int)): | ||||
|                 k = Const(k) | ||||
|             if (not isinstance(k, Const) | ||||
|                     and not (isinstance(k, str) and k == "default")): | ||||
|                 raise TypeError("Case object is not a Migen constant") | ||||
|             if isinstance(k, str) and k == "default": | ||||
|                 k = "-" * len(ast.Value.wrap(test)) | ||||
|             else: | ||||
|                 k = k.value | ||||
|             new_cases.append((k, v)) | ||||
|         super().__init__(test, OrderedDict(new_cases)) | ||||
| 
 | ||||
|     @deprecated("instead of `Case(...).makedefault()`, use an explicit default case: " | ||||
|                 "`with m.Case(): ...`") | ||||
|     def makedefault(self, key=None): | ||||
|         raise NotImplementedError | ||||
|         if key is None: | ||||
|             for choice in self.cases.keys(): | ||||
|                 if (key is None | ||||
|                         or (isinstance(choice, str) and choice == "default") | ||||
|                         or choice > key): | ||||
|                     key = choice | ||||
|         if isinstance(key, str) and key == "default": | ||||
|             key = "-" * len(self.test) | ||||
|         else: | ||||
|             key = "{:0{}b}".format(wrap(key).value, len(self.test)) | ||||
|         stmts = self.cases[key] | ||||
|         del self.cases[key] | ||||
|         self.cases["-" * len(self.test)] = stmts | ||||
|         return self | ||||
| 
 | ||||
| 
 | ||||
| def Array(*args): | ||||
|  |  | |||
							
								
								
									
										187
									
								
								nmigen/compat/genlib/fsm.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								nmigen/compat/genlib/fsm.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,187 @@ | |||
| import warnings | ||||
| from collections import OrderedDict | ||||
| 
 | ||||
| from ...fhdl.xfrm import ValueTransformer, StatementTransformer | ||||
| from ...fhdl.ast import * | ||||
| from ..fhdl.module import CompatModule, CompatFinalizeError | ||||
| from ..fhdl.structure import If, Case | ||||
| 
 | ||||
| 
 | ||||
| __all__ = ["AnonymousState", "NextState", "NextValue", "FSM"] | ||||
| 
 | ||||
| 
 | ||||
| class AnonymousState: | ||||
|     pass | ||||
| 
 | ||||
| 
 | ||||
| class NextState(Statement): | ||||
|     def __init__(self, state): | ||||
|         self.state = state | ||||
| 
 | ||||
| 
 | ||||
| class NextValue(Statement): | ||||
|     def __init__(self, target, value): | ||||
|         self.target = target | ||||
|         self.value = value | ||||
| 
 | ||||
| 
 | ||||
| def _target_eq(a, b): | ||||
|     if type(a) != type(b): | ||||
|         return False | ||||
|     ty = type(a) | ||||
|     if ty == Const: | ||||
|         return a.value == b.value | ||||
|     elif ty == Signal: | ||||
|         return a is b | ||||
|     elif ty == Cat: | ||||
|         return all(_target_eq(x, y) for x, y in zip(a.l, b.l)) | ||||
|     elif ty == Slice: | ||||
|         return (_target_eq(a.value, b.value) | ||||
|                     and a.start == b.start | ||||
|                     and a.stop == b.stop) | ||||
|     elif ty == Part: | ||||
|         return (_target_eq(a.value, b.value) | ||||
|                     and _target_eq(a.offset == b.offset) | ||||
|                     and a.width == b.width) | ||||
|     elif ty == ArrayProxy: | ||||
|         return (all(_target_eq(x, y) for x, y in zip(a.choices, b.choices)) | ||||
|                     and _target_eq(a.key, b.key)) | ||||
|     else: | ||||
|         raise ValueError("NextValue cannot be used with target type '{}'" | ||||
|                          .format(ty)) | ||||
| 
 | ||||
| 
 | ||||
| class _LowerNext(ValueTransformer, StatementTransformer): | ||||
|     def __init__(self, next_state_signal, encoding, aliases): | ||||
|         self.next_state_signal = next_state_signal | ||||
|         self.encoding = encoding | ||||
|         self.aliases = aliases | ||||
|         # (target, next_value_ce, next_value) | ||||
|         self.registers = [] | ||||
| 
 | ||||
|     def _get_register_control(self, target): | ||||
|         for x in self.registers: | ||||
|             if _target_eq(target, x[0]): | ||||
|                 return x[1], x[2] | ||||
|         raise KeyError | ||||
| 
 | ||||
|     def on_unknown_statement(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]) | ||||
|         elif isinstance(node, NextValue): | ||||
|             try: | ||||
|                 next_value_ce, next_value = self._get_register_control(node.target) | ||||
|             except KeyError: | ||||
|                 related = node.target if isinstance(node.target, Signal) else None | ||||
|                 next_value = Signal(node.target.shape()) | ||||
|                 next_value_ce = Signal() | ||||
|                 self.registers.append((node.target, next_value_ce, next_value)) | ||||
|             return next_value.eq(node.value), next_value_ce.eq(1) | ||||
|         else: | ||||
|             return node | ||||
| 
 | ||||
| 
 | ||||
| class FSM(CompatModule): | ||||
|     def __init__(self, reset_state=None): | ||||
|         self.actions = OrderedDict() | ||||
|         self.state_aliases = dict() | ||||
|         self.reset_state = reset_state | ||||
| 
 | ||||
|         self.before_entering_signals = OrderedDict() | ||||
|         self.before_leaving_signals = OrderedDict() | ||||
|         self.after_entering_signals = OrderedDict() | ||||
|         self.after_leaving_signals = OrderedDict() | ||||
| 
 | ||||
|     def act(self, state, *statements): | ||||
|         if self.finalized: | ||||
|             raise CompatFinalizeError | ||||
|         if self.reset_state is None: | ||||
|             self.reset_state = state | ||||
|         if state not in self.actions: | ||||
|             self.actions[state] = [] | ||||
|         self.actions[state] += statements | ||||
| 
 | ||||
|     def delayed_enter(self, name, target, delay): | ||||
|         if self.finalized: | ||||
|             raise CompatFinalizeError | ||||
|         if delay > 0: | ||||
|             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 | ||||
| 
 | ||||
|     def ongoing(self, state): | ||||
|         is_ongoing = Signal() | ||||
|         self.act(state, is_ongoing.eq(1)) | ||||
|         return is_ongoing | ||||
| 
 | ||||
|     def _get_signal(self, d, state): | ||||
|         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 | ||||
| 
 | ||||
|     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 | ||||
| 
 | ||||
|     def do_finalize(self): | ||||
|         nstates = len(self.actions) | ||||
|         self.encoding = dict((s, n) for n, s in enumerate(self.actions.keys())) | ||||
|         self.decoding = {n: s for s, n in self.encoding.items()} | ||||
| 
 | ||||
|         self.state = Signal(max=nstates, reset=self.encoding[self.reset_state]) | ||||
|         self.state._enumeration = self.decoding | ||||
|         self.next_state = Signal(max=nstates) | ||||
|         self.next_state._enumeration = {n: "{}:{}".format(n, s) for n, s in self.decoding.items()} | ||||
| 
 | ||||
|         for state, signal in self.before_leaving_signals.items(): | ||||
|             encoded = self.encoding[state] | ||||
|             self.comb += signal.eq((self.state == encoded) & ~(self.next_state == encoded)) | ||||
|         if self.reset_state in self.after_entering_signals: | ||||
|             self.after_entering_signals[self.reset_state].reset = 1 | ||||
|         for state, signal in self.before_entering_signals.items(): | ||||
|             encoded = self.encoding[state] | ||||
|             self.comb += signal.eq(~(self.state == encoded) & (self.next_state == encoded)) | ||||
| 
 | ||||
|         self._finalize_sync(self._lower_controls()) | ||||
| 
 | ||||
|     def _lower_controls(self): | ||||
|         return _LowerNext(self.next_state, self.encoding, self.state_aliases) | ||||
| 
 | ||||
|     def _finalize_sync(self, ls): | ||||
|         cases = dict((self.encoding[k], ls.on_statement(v)) for k, v in self.actions.items() if v) | ||||
|         with warnings.catch_warnings(): | ||||
|             self.comb += [ | ||||
|                 self.next_state.eq(self.state), | ||||
|                 Case(self.state, cases).makedefault(self.encoding[self.reset_state]) | ||||
|             ] | ||||
|             self.sync += self.state.eq(self.next_state) | ||||
|             for register, next_value_ce, next_value in ls.registers: | ||||
|                 self.sync += If(next_value_ce, register.eq(next_value)) | ||||
|  | @ -1,5 +1,6 @@ | |||
| from collections import OrderedDict | ||||
| from collections import OrderedDict, Iterable | ||||
| 
 | ||||
| from ..tools import flatten | ||||
| from .ast import * | ||||
| from .ir import * | ||||
| 
 | ||||
|  | @ -36,6 +37,9 @@ class ValueTransformer: | |||
|     def on_Repl(self, value): | ||||
|         return Repl(self.on_value(value.value), value.count) | ||||
| 
 | ||||
|     def on_unknown_value(self, value): | ||||
|         raise TypeError("Cannot transform value {!r}".format(value)) # :nocov: | ||||
| 
 | ||||
|     def on_value(self, value): | ||||
|         if isinstance(value, Const): | ||||
|             new_value = self.on_Const(value) | ||||
|  | @ -56,7 +60,7 @@ class ValueTransformer: | |||
|         elif isinstance(value, Repl): | ||||
|             new_value = self.on_Repl(value) | ||||
|         else: | ||||
|             raise TypeError("Cannot transform value {!r}".format(value)) # :nocov: | ||||
|             new_value = self.on_unknown_value(value) | ||||
|         if isinstance(new_value, Value): | ||||
|             new_value.src_loc = value.src_loc | ||||
|         return new_value | ||||
|  | @ -73,21 +77,24 @@ class StatementTransformer: | |||
|         return Assign(self.on_value(stmt.lhs), self.on_value(stmt.rhs)) | ||||
| 
 | ||||
|     def on_Switch(self, stmt): | ||||
|         cases = OrderedDict((k, self.on_value(v)) for k, v in stmt.cases.items()) | ||||
|         cases = OrderedDict((k, self.on_statement(v)) for k, v in stmt.cases.items()) | ||||
|         return Switch(self.on_value(stmt.test), cases) | ||||
| 
 | ||||
|     def on_statements(self, stmt): | ||||
|         return list(flatten(self.on_statement(stmt) for stmt in self.on_statement(stmt))) | ||||
|         return list(flatten(self.on_statement(stmt) for stmt in stmt)) | ||||
| 
 | ||||
|     def on_unknown_statement(self, stmt): | ||||
|         raise TypeError("Cannot transform statement {!r}".format(stmt)) # :nocov: | ||||
| 
 | ||||
|     def on_statement(self, stmt): | ||||
|         if isinstance(stmt, Assign): | ||||
|             return self.on_Assign(stmt) | ||||
|         elif isinstance(stmt, Switch): | ||||
|             return self.on_Switch(stmt) | ||||
|         elif isinstance(stmt, (list, tuple)): | ||||
|         elif isinstance(stmt, Iterable): | ||||
|             return self.on_statements(stmt) | ||||
|         else: | ||||
|             raise TypeError("Cannot transform statement {!r}".format(stmt)) # :nocov: | ||||
|             return self.on_unknown_statement(stmt) | ||||
| 
 | ||||
|     def __call__(self, value): | ||||
|         return self.on_statement(value) | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 whitequark
						whitequark