Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

FSM: new API

  • Loading branch information...
commit d0caa738bd262a2ad4bb7d083d366e54bb02c840 1 parent b56cb3c
Sébastien Bourdeauducq authored June 25, 2013
8  examples/basic/fsm.py
... ...
@@ -1,14 +1,14 @@
1 1
 from migen.fhdl.std import *
2 2
 from migen.fhdl import verilog
3  
-from migen.genlib.fsm import FSM
  3
+from migen.genlib.fsm import FSM, NextState
4 4
 
5 5
 class Example(Module):
6 6
 	def __init__(self):
7 7
 		self.s = Signal()
8  
-		myfsm = FSM("FOO", "BAR")
  8
+		myfsm = FSM()
9 9
 		self.submodules += myfsm
10  
-		myfsm.act(myfsm.FOO, self.s.eq(1), myfsm.next_state(myfsm.BAR))
11  
-		myfsm.act(myfsm.BAR, self.s.eq(0), myfsm.next_state(myfsm.FOO))
  10
+		myfsm.act("FOO", self.s.eq(1), NextState("BAR"))
  11
+		myfsm.act("BAR", self.s.eq(0), NextState("FOO"))
12 12
 
13 13
 example = Example()
14 14
 print(verilog.convert(example, {example.s}))
10  migen/actorlib/misc.py
@@ -47,18 +47,18 @@ def __init__(self, nbits, offsetbits=0, step=1):
47 47
 		else:
48 48
 			self.comb += self.source.payload.value.eq(counter)
49 49
 		
50  
-		fsm = FSM("IDLE", "ACTIVE")
  50
+		fsm = FSM()
51 51
 		self.submodules += fsm
52  
-		fsm.act(fsm.IDLE,
  52
+		fsm.act("IDLE",
53 53
 			load.eq(1),
54 54
 			self.parameters.ack.eq(1),
55  
-			If(self.parameters.stb, fsm.next_state(fsm.ACTIVE))
  55
+			If(self.parameters.stb, NextState("ACTIVE"))
56 56
 		)
57  
-		fsm.act(fsm.ACTIVE,
  57
+		fsm.act("ACTIVE",
58 58
 			self.busy.eq(1),
59 59
 			self.source.stb.eq(1),
60 60
 			If(self.source.ack,
61 61
 				ce.eq(1),
62  
-				If(last, fsm.next_state(fsm.IDLE))
  62
+				If(last, NextState("IDLE"))
63 63
 			)
64 64
 		)
42  migen/bus/wishbone2asmi.py
... ...
@@ -1,6 +1,6 @@
1 1
 from migen.fhdl.std import *
2 2
 from migen.bus import wishbone
3  
-from migen.genlib.fsm import FSM
  3
+from migen.genlib.fsm import FSM, NextState
4 4
 from migen.genlib.misc import split, displacer, chooser
5 5
 from migen.genlib.record import Record, layout_len
6 6
 
@@ -79,60 +79,58 @@ def get_fragment(self):
79 79
 		write_to_asmi_pre = Signal()
80 80
 		sync.append(write_to_asmi.eq(write_to_asmi_pre))
81 81
 		
82  
-		fsm = FSM("IDLE", "TEST_HIT",
83  
-			"EVICT_ISSUE", "EVICT_WAIT",
84  
-			"REFILL_WRTAG", "REFILL_ISSUE", "REFILL_WAIT", "REFILL_COMPLETE")
  82
+		fsm = FSM()
85 83
 		
86  
-		fsm.act(fsm.IDLE,
87  
-			If(self.wishbone.cyc & self.wishbone.stb, fsm.next_state(fsm.TEST_HIT))
  84
+		fsm.act("IDLE",
  85
+			If(self.wishbone.cyc & self.wishbone.stb, NextState("TEST_HIT"))
88 86
 		)
89  
-		fsm.act(fsm.TEST_HIT,
  87
+		fsm.act("TEST_HIT",
90 88
 			If(tag_do.tag == adr_tag,
91 89
 				self.wishbone.ack.eq(1),
92 90
 				If(self.wishbone.we,
93 91
 					tag_di.dirty.eq(1),
94 92
 					tag_port.we.eq(1)
95 93
 				),
96  
-				fsm.next_state(fsm.IDLE)
  94
+				NextState("IDLE")
97 95
 			).Else(
98 96
 				If(tag_do.dirty,
99  
-					fsm.next_state(fsm.EVICT_ISSUE)
  97
+					NextState("EVICT_ISSUE")
100 98
 				).Else(
101  
-					fsm.next_state(fsm.REFILL_WRTAG)
  99
+					NextState("REFILL_WRTAG")
102 100
 				)
103 101
 			)
104 102
 		)
105 103
 		
106  
-		fsm.act(fsm.EVICT_ISSUE,
  104
+		fsm.act("EVICT_ISSUE",
107 105
 			self.asmiport.stb.eq(1),
108 106
 			self.asmiport.we.eq(1),
109  
-			If(self.asmiport.ack, fsm.next_state(fsm.EVICT_WAIT))
  107
+			If(self.asmiport.ack, NextState("EVICT_WAIT"))
110 108
 		)
111  
-		fsm.act(fsm.EVICT_WAIT,
  109
+		fsm.act("EVICT_WAIT",
112 110
 			# Data is actually sampled by the memory controller in the next state.
113 111
 			# But since the data memory has one cycle latency, it gets the data
114 112
 			# at the address given during this cycle.
115 113
 			If(self.asmiport.get_call_expression(),
116 114
 				write_to_asmi_pre.eq(1),
117  
-				fsm.next_state(fsm.REFILL_WRTAG)
  115
+				NextState("REFILL_WRTAG")
118 116
 			)
119 117
 		)
120 118
 		
121  
-		fsm.act(fsm.REFILL_WRTAG,
  119
+		fsm.act("REFILL_WRTAG",
122 120
 			# Write the tag first to set the ASMI address
123 121
 			tag_port.we.eq(1),
124  
-			fsm.next_state(fsm.REFILL_ISSUE)
  122
+			NextState("REFILL_ISSUE")
125 123
 		)
126  
-		fsm.act(fsm.REFILL_ISSUE,
  124
+		fsm.act("REFILL_ISSUE",
127 125
 			self.asmiport.stb.eq(1),
128  
-			If(self.asmiport.ack, fsm.next_state(fsm.REFILL_WAIT))
  126
+			If(self.asmiport.ack, NextState("REFILL_WAIT"))
129 127
 		)
130  
-		fsm.act(fsm.REFILL_WAIT,
131  
-			If(self.asmiport.get_call_expression(), fsm.next_state(fsm.REFILL_COMPLETE))
  128
+		fsm.act("REFILL_WAIT",
  129
+			If(self.asmiport.get_call_expression(), NextState("REFILL_COMPLETE"))
132 130
 		)
133  
-		fsm.act(fsm.REFILL_COMPLETE,
  131
+		fsm.act("REFILL_COMPLETE",
134 132
 			write_from_asmi.eq(1),
135  
-			fsm.next_state(fsm.TEST_HIT)
  133
+			NextState("TEST_HIT")
136 134
 		)
137 135
 		
138 136
 		return Fragment(comb, sync, specials={data_mem, tag_mem, data_port, tag_port}) \
53  migen/bus/wishbone2lasmi.py
... ...
@@ -1,6 +1,6 @@
1 1
 from migen.fhdl.std import *
2 2
 from migen.bus import wishbone
3  
-from migen.genlib.fsm import FSM
  3
+from migen.genlib.fsm import FSM, NextState
4 4
 from migen.genlib.misc import split, displacer, chooser
5 5
 from migen.genlib.record import Record, layout_len
6 6
 
@@ -71,61 +71,58 @@ def __init__(self, cachesize, lasmim):
71 71
 		
72 72
 		# Control FSM
73 73
 		assert(lasmim.write_latency >= 1 and lasmim.read_latency >= 1)
74  
-		fsm = FSM("IDLE", "TEST_HIT",
75  
-			"EVICT_REQUEST", "EVICT_WAIT_DATA_ACK", "EVICT_DATA",
76  
-			"REFILL_WRTAG", "REFILL_REQUEST", "REFILL_WAIT_DATA_ACK", "REFILL_DATA",
77  
-			delayed_enters=[
78  
-				("EVICT_DATAD", "EVICT_DATA", lasmim.write_latency-1),
79  
-				("REFILL_DATAD", "REFILL_DATA", lasmim.read_latency-1)
80  
-			])
  74
+		fsm = FSM()
81 75
 		self.submodules += fsm
82 76
 		
83  
-		fsm.act(fsm.IDLE,
84  
-			If(self.wishbone.cyc & self.wishbone.stb, fsm.next_state(fsm.TEST_HIT))
  77
+		fsm.delayed_enter("EVICT_DATAD", "EVICT_DATA", lasmim.write_latency-1)
  78
+		fsm.delayed_enter("REFILL_DATAD", "REFILL_DATA", lasmim.read_latency-1)
  79
+
  80
+		fsm.act("IDLE",
  81
+			If(self.wishbone.cyc & self.wishbone.stb, NextState("TEST_HIT"))
85 82
 		)
86  
-		fsm.act(fsm.TEST_HIT,
  83
+		fsm.act("TEST_HIT",
87 84
 			If(tag_do.tag == adr_tag,
88 85
 				self.wishbone.ack.eq(1),
89 86
 				If(self.wishbone.we,
90 87
 					tag_di.dirty.eq(1),
91 88
 					tag_port.we.eq(1)
92 89
 				),
93  
-				fsm.next_state(fsm.IDLE)
  90
+				NextState("IDLE")
94 91
 			).Else(
95 92
 				If(tag_do.dirty,
96  
-					fsm.next_state(fsm.EVICT_REQUEST)
  93
+					NextState("EVICT_REQUEST")
97 94
 				).Else(
98  
-					fsm.next_state(fsm.REFILL_WRTAG)
  95
+					NextState("REFILL_WRTAG")
99 96
 				)
100 97
 			)
101 98
 		)
102 99
 		
103  
-		fsm.act(fsm.EVICT_REQUEST,
  100
+		fsm.act("EVICT_REQUEST",
104 101
 			lasmim.stb.eq(1),
105 102
 			lasmim.we.eq(1),
106  
-			If(lasmim.req_ack, fsm.next_state(fsm.EVICT_WAIT_DATA_ACK))
  103
+			If(lasmim.req_ack, NextState("EVICT_WAIT_DATA_ACK"))
107 104
 		)
108  
-		fsm.act(fsm.EVICT_WAIT_DATA_ACK,
109  
-			If(lasmim.dat_ack, fsm.next_state(fsm.EVICT_DATAD))
  105
+		fsm.act("EVICT_WAIT_DATA_ACK",
  106
+			If(lasmim.dat_ack, NextState("EVICT_DATAD"))
110 107
 		)
111  
-		fsm.act(fsm.EVICT_DATA,
  108
+		fsm.act("EVICT_DATA",
112 109
 			write_to_lasmi.eq(1),
113  
-			fsm.next_state(fsm.REFILL_WRTAG)
  110
+			NextState("REFILL_WRTAG")
114 111
 		)
115 112
 		
116  
-		fsm.act(fsm.REFILL_WRTAG,
  113
+		fsm.act("REFILL_WRTAG",
117 114
 			# Write the tag first to set the LASMI address
118 115
 			tag_port.we.eq(1),
119  
-			fsm.next_state(fsm.REFILL_REQUEST)
  116
+			NextState("REFILL_REQUEST")
120 117
 		)
121  
-		fsm.act(fsm.REFILL_REQUEST,
  118
+		fsm.act("REFILL_REQUEST",
122 119
 			lasmim.stb.eq(1),
123  
-			If(lasmim.req_ack, fsm.next_state(fsm.REFILL_WAIT_DATA_ACK))
  120
+			If(lasmim.req_ack, NextState("REFILL_WAIT_DATA_ACK"))
124 121
 		)
125  
-		fsm.act(fsm.REFILL_WAIT_DATA_ACK,
126  
-			If(lasmim.dat_ack, fsm.next_state(fsm.REFILL_DATAD))
  122
+		fsm.act("REFILL_WAIT_DATA_ACK",
  123
+			If(lasmim.dat_ack, NextState("REFILL_DATAD"))
127 124
 		)
128  
-		fsm.act(fsm.REFILL_DATA,
  125
+		fsm.act("REFILL_DATA",
129 126
 			write_from_lasmi.eq(1),
130  
-			fsm.next_state(fsm.TEST_HIT)
  127
+			NextState("TEST_HIT")
131 128
 		)
100  migen/genlib/fsm.py
... ...
@@ -1,41 +1,73 @@
  1
+from collections import OrderedDict
  2
+
1 3
 from migen.fhdl.std import *
  4
+from migen.fhdl.module import FinalizeError
  5
+from migen.fhdl.visit import NodeTransformer
2 6
 
3  
-class FSM:
4  
-	def __init__(self, *states, delayed_enters=[]):
5  
-		nstates = len(states) + sum([d[2] for d in delayed_enters])
6  
-		
7  
-		self._state = Signal(max=nstates)
8  
-		self._next_state = Signal(max=nstates)
9  
-		for n, state in enumerate(states):
10  
-			setattr(self, state, n)
11  
-		self.actions = [[] for i in range(len(states))]
  7
+class AnonymousState:
  8
+	pass
  9
+
  10
+# do not use namedtuple here as it inherits tuple
  11
+# and the latter is used elsewhere in FHDL
  12
+class NextState:
  13
+	def __init__(self, state):
  14
+		self.state = state
  15
+
  16
+class _LowerNextState(NodeTransformer):
  17
+	def __init__(self, next_state_signal, encoding, aliases):
  18
+		self.next_state_signal = next_state_signal
  19
+		self.encoding = encoding
  20
+		self.aliases = aliases
12 21
 		
13  
-		for name, target, delay in delayed_enters:
14  
-			target_state = getattr(self, target)
15  
-			if delay:
16  
-				name_state = len(self.actions)
17  
-				setattr(self, name, name_state)
18  
-				for i in range(delay-1):
19  
-					self.actions.append([self.next_state(name_state+i+1)])
20  
-				self.actions.append([self.next_state(target_state)])
21  
-			else:
22  
-				# alias
23  
-				setattr(self, name, target_state)
24  
-	
25  
-	def reset_state(self, state):
26  
-		self._state.reset = state
27  
-	
28  
-	def next_state(self, state):
29  
-		return self._next_state.eq(state)
30  
-	
  22
+	def visit_unknown(self, node):
  23
+		if isinstance(node, NextState):
  24
+			try:
  25
+				actual_state = self.aliases[node.state]
  26
+			except KeyError:
  27
+				actual_state = node.state
  28
+			return self.next_state_signal.eq(self.encoding[actual_state])
  29
+		else:
  30
+			return node
  31
+
  32
+class FSM(Module):
  33
+	def __init__(self):
  34
+		self.actions = OrderedDict()
  35
+		self.state_aliases = dict()
  36
+		self.reset_state = None
  37
+
31 38
 	def act(self, state, *statements):
  39
+		if self.finalized:
  40
+			raise FinalizeError
  41
+		if state not in self.actions:
  42
+			self.actions[state] = []
32 43
 		self.actions[state] += statements
  44
+
  45
+	def delayed_enter(self, name, target, delay):
  46
+		if self.finalized:
  47
+			raise FinalizeError
  48
+		if delay:
  49
+			state = name
  50
+			for i in range(delay):
  51
+				if i == delay - 1:
  52
+					next_state = target
  53
+				else:
  54
+					next_state = AnonymousState()
  55
+				self.act(state, NextState(next_state))
  56
+				state = next_state
  57
+		else:
  58
+			self.state_aliases[name] = target
33 59
 	
34  
-	def get_fragment(self):
35  
-		cases = dict((s, a) for s, a in enumerate(self.actions) if a)
36  
-		comb = [
37  
-			self._next_state.eq(self._state),
38  
-			Case(self._state, cases)
  60
+	def do_finalize(self):
  61
+		nstates = len(self.actions)
  62
+
  63
+		self.encoding = dict((s, n) for n, s in enumerate(self.actions.keys()))
  64
+		self.state = Signal(max=nstates)
  65
+		self.next_state = Signal(max=nstates)
  66
+
  67
+		lns = _LowerNextState(self.next_state, self.encoding, self.state_aliases)
  68
+		cases = dict((self.encoding[k], lns.visit(v)) for k, v in self.actions.items() if v)
  69
+		self.comb += [
  70
+			self.next_state.eq(self.state),
  71
+			Case(self.state, cases)
39 72
 		]
40  
-		sync = [self._state.eq(self._next_state)]
41  
-		return Fragment(comb, sync)
  73
+		self.sync += self.state.eq(self.next_state)
10  migen/pytholite/compiler.py
@@ -166,10 +166,10 @@ def visit_if(self, sa, node):
166 166
 		states_f, exit_states_f = self.visit_block(node.orelse)
167 167
 		exit_states = exit_states_t + exit_states_f
168 168
 		
169  
-		test_state_stmt = If(test, AbstractNextState(states_t[0]))
  169
+		test_state_stmt = If(test, id_next_state(states_t[0]))
170 170
 		test_state = [test_state_stmt]
171 171
 		if states_f:
172  
-			test_state_stmt.Else(AbstractNextState(states_f[0]))
  172
+			test_state_stmt.Else(id_next_state(states_f[0]))
173 173
 		else:
174 174
 			exit_states.append(test_state)
175 175
 		
@@ -180,9 +180,9 @@ def visit_while(self, sa, node):
180 180
 		test = self.ec.visit_expr(node.test)
181 181
 		states_b, exit_states_b = self.visit_block(node.body)
182 182
 
183  
-		test_state = [If(test, AbstractNextState(states_b[0]))]
  183
+		test_state = [If(test, id_next_state(states_b[0]))]
184 184
 		for exit_state in exit_states_b:
185  
-			exit_state.insert(0, AbstractNextState(test_state))
  185
+			exit_state.insert(0, id_next_state(test_state))
186 186
 		
187 187
 		sa.assemble([test_state] + states_b, [test_state])
188 188
 	
@@ -199,7 +199,7 @@ def visit_for(self, sa, node):
199 199
 			self.symdict[target] = iteration
200 200
 			states_b, exit_states_b = self.visit_block(node.body)
201 201
 			for exit_state in last_exit_states:
202  
-				exit_state.insert(0, AbstractNextState(states_b[0]))
  202
+				exit_state.insert(0, id_next_state(states_b[0]))
203 203
 			last_exit_states = exit_states_b
204 204
 			states += states_b
205 205
 		del self.symdict[target]
39  migen/pytholite/fsm.py
... ...
@@ -1,9 +1,7 @@
1  
-from migen.fhdl import visit as fhdl
2  
-from migen.genlib.fsm import FSM
  1
+from migen.genlib.fsm import FSM, NextState
3 2
 
4  
-class AbstractNextState:
5  
-	def __init__(self, target_state):
6  
-		self.target_state = target_state
  3
+def id_next_state(l):
  4
+	return NextState(id(l))
7 5
 
8 6
 # entry state is first state returned
9 7
 class StateAssembler:
@@ -14,37 +12,14 @@ def __init__(self):
14 12
 	def assemble(self, n_states, n_exit_states):
15 13
 		self.states += n_states
16 14
 		for exit_state in self.exit_states:
17  
-			exit_state.insert(0, AbstractNextState(n_states[0]))
  15
+			exit_state.insert(0, id_next_state(n_states[0]))
18 16
 		self.exit_states = n_exit_states
19 17
 	
20 18
 	def ret(self):
21 19
 		return self.states, self.exit_states
22 20
 
23  
-# like list.index, but using "is" instead of comparison
24  
-def _index_is(l, x):
25  
-	for i, e in enumerate(l):
26  
-		if e is x:
27  
-			return i
28  
-
29  
-class _LowerAbstractNextState(fhdl.NodeTransformer):
30  
-	def __init__(self, fsm, states, stnames):
31  
-		self.fsm = fsm
32  
-		self.states = states
33  
-		self.stnames = stnames
34  
-		
35  
-	def visit_unknown(self, node):
36  
-		if isinstance(node, AbstractNextState):
37  
-			index = _index_is(self.states, node.target_state)
38  
-			estate = getattr(self.fsm, self.stnames[index])
39  
-			return self.fsm.next_state(estate)
40  
-		else:
41  
-			return node
42  
-
43 21
 def implement_fsm(states):
44  
-	stnames = ["S" + str(i) for i in range(len(states))]
45  
-	fsm = FSM(*stnames)
46  
-	lans = _LowerAbstractNextState(fsm, states, stnames)
47  
-	for i, state in enumerate(states):
48  
-		actions = lans.visit(state)
49  
-		fsm.act(getattr(fsm, stnames[i]), *actions)
  22
+	fsm = FSM()
  23
+	for state in states:
  24
+		fsm.act(id(state), state)
50 25
 	return fsm
8  migen/pytholite/io.py
@@ -50,7 +50,7 @@ def _gen_df_io(compiler, modelname, to_model, from_model):
50 50
 			state += [reg.load(cexpr) for reg in target_regs]
51 51
 		state += [
52 52
 			ep.ack.eq(1),
53  
-			If(~ep.stb, AbstractNextState(state))
  53
+			If(~ep.stb, id_next_state(state))
54 54
 		]
55 55
 		return [state], [state]
56 56
 	else:
@@ -67,7 +67,7 @@ def _gen_df_io(compiler, modelname, to_model, from_model):
67 67
 			state.append(signal.eq(compiler.ec.visit_expr(value)))
68 68
 		state += [
69 69
 			ep.stb.eq(1),
70  
-			If(~ep.ack, AbstractNextState(state))
  70
+			If(~ep.ack, id_next_state(state))
71 71
 		]
72 72
 		return [state], [state]
73 73
 
@@ -110,7 +110,7 @@ def _gen_wishbone_io(compiler, modelname, model, to_model, from_model, bus):
110 110
 		for target_regs, expr in from_model:
111 111
 			cexpr = ec.visit_expr(expr)
112 112
 			state += [reg.load(cexpr) for reg in target_regs]
113  
-	state.append(If(~bus.ack, AbstractNextState(state)))
  113
+	state.append(If(~bus.ack, id_next_state(state)))
114 114
 	return [state], [state]
115 115
 
116 116
 def _gen_memory_io(compiler, modelname, model, to_model, from_model, port):
@@ -128,7 +128,7 @@ def _gen_memory_io(compiler, modelname, model, to_model, from_model, port):
128 128
 		return [s1], [s1]
129 129
 	else:
130 130
 		s2 = []
131  
-		s1.append(AbstractNextState(s2))
  131
+		s1.append(id_next_state(s2))
132 132
 		ec = _BusReadExprCompiler(compiler.symdict, modelname, port.dat_r)
133 133
 		for target_regs, expr in from_model:
134 134
 			cexpr = ec.visit_expr(expr)

0 notes on commit d0caa73

Please sign in to comment.
Something went wrong with that request. Please try again.