Skip to content

Commit

Permalink
FSM: new API
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastien Bourdeauducq committed Jun 25, 2013
1 parent b56cb3c commit d0caa73
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 134 deletions.
8 changes: 4 additions & 4 deletions examples/basic/fsm.py
@@ -1,14 +1,14 @@
from migen.fhdl.std import *
from migen.fhdl import verilog
from migen.genlib.fsm import FSM
from migen.genlib.fsm import FSM, NextState

class Example(Module):
def __init__(self):
self.s = Signal()
myfsm = FSM("FOO", "BAR")
myfsm = FSM()
self.submodules += myfsm
myfsm.act(myfsm.FOO, self.s.eq(1), myfsm.next_state(myfsm.BAR))
myfsm.act(myfsm.BAR, self.s.eq(0), myfsm.next_state(myfsm.FOO))
myfsm.act("FOO", self.s.eq(1), NextState("BAR"))
myfsm.act("BAR", self.s.eq(0), NextState("FOO"))

example = Example()
print(verilog.convert(example, {example.s}))
10 changes: 5 additions & 5 deletions migen/actorlib/misc.py
Expand Up @@ -47,18 +47,18 @@ def __init__(self, nbits, offsetbits=0, step=1):
else:
self.comb += self.source.payload.value.eq(counter)

fsm = FSM("IDLE", "ACTIVE")
fsm = FSM()
self.submodules += fsm
fsm.act(fsm.IDLE,
fsm.act("IDLE",
load.eq(1),
self.parameters.ack.eq(1),
If(self.parameters.stb, fsm.next_state(fsm.ACTIVE))
If(self.parameters.stb, NextState("ACTIVE"))
)
fsm.act(fsm.ACTIVE,
fsm.act("ACTIVE",
self.busy.eq(1),
self.source.stb.eq(1),
If(self.source.ack,
ce.eq(1),
If(last, fsm.next_state(fsm.IDLE))
If(last, NextState("IDLE"))
)
)
42 changes: 20 additions & 22 deletions migen/bus/wishbone2asmi.py
@@ -1,6 +1,6 @@
from migen.fhdl.std import *
from migen.bus import wishbone
from migen.genlib.fsm import FSM
from migen.genlib.fsm import FSM, NextState
from migen.genlib.misc import split, displacer, chooser
from migen.genlib.record import Record, layout_len

Expand Down Expand Up @@ -79,60 +79,58 @@ def get_fragment(self):
write_to_asmi_pre = Signal()
sync.append(write_to_asmi.eq(write_to_asmi_pre))

fsm = FSM("IDLE", "TEST_HIT",
"EVICT_ISSUE", "EVICT_WAIT",
"REFILL_WRTAG", "REFILL_ISSUE", "REFILL_WAIT", "REFILL_COMPLETE")
fsm = FSM()

fsm.act(fsm.IDLE,
If(self.wishbone.cyc & self.wishbone.stb, fsm.next_state(fsm.TEST_HIT))
fsm.act("IDLE",
If(self.wishbone.cyc & self.wishbone.stb, NextState("TEST_HIT"))
)
fsm.act(fsm.TEST_HIT,
fsm.act("TEST_HIT",
If(tag_do.tag == adr_tag,
self.wishbone.ack.eq(1),
If(self.wishbone.we,
tag_di.dirty.eq(1),
tag_port.we.eq(1)
),
fsm.next_state(fsm.IDLE)
NextState("IDLE")
).Else(
If(tag_do.dirty,
fsm.next_state(fsm.EVICT_ISSUE)
NextState("EVICT_ISSUE")
).Else(
fsm.next_state(fsm.REFILL_WRTAG)
NextState("REFILL_WRTAG")
)
)
)

fsm.act(fsm.EVICT_ISSUE,
fsm.act("EVICT_ISSUE",
self.asmiport.stb.eq(1),
self.asmiport.we.eq(1),
If(self.asmiport.ack, fsm.next_state(fsm.EVICT_WAIT))
If(self.asmiport.ack, NextState("EVICT_WAIT"))
)
fsm.act(fsm.EVICT_WAIT,
fsm.act("EVICT_WAIT",
# Data is actually sampled by the memory controller in the next state.
# But since the data memory has one cycle latency, it gets the data
# at the address given during this cycle.
If(self.asmiport.get_call_expression(),
write_to_asmi_pre.eq(1),
fsm.next_state(fsm.REFILL_WRTAG)
NextState("REFILL_WRTAG")
)
)

fsm.act(fsm.REFILL_WRTAG,
fsm.act("REFILL_WRTAG",
# Write the tag first to set the ASMI address
tag_port.we.eq(1),
fsm.next_state(fsm.REFILL_ISSUE)
NextState("REFILL_ISSUE")
)
fsm.act(fsm.REFILL_ISSUE,
fsm.act("REFILL_ISSUE",
self.asmiport.stb.eq(1),
If(self.asmiport.ack, fsm.next_state(fsm.REFILL_WAIT))
If(self.asmiport.ack, NextState("REFILL_WAIT"))
)
fsm.act(fsm.REFILL_WAIT,
If(self.asmiport.get_call_expression(), fsm.next_state(fsm.REFILL_COMPLETE))
fsm.act("REFILL_WAIT",
If(self.asmiport.get_call_expression(), NextState("REFILL_COMPLETE"))
)
fsm.act(fsm.REFILL_COMPLETE,
fsm.act("REFILL_COMPLETE",
write_from_asmi.eq(1),
fsm.next_state(fsm.TEST_HIT)
NextState("TEST_HIT")
)

return Fragment(comb, sync, specials={data_mem, tag_mem, data_port, tag_port}) \
Expand Down
53 changes: 25 additions & 28 deletions migen/bus/wishbone2lasmi.py
@@ -1,6 +1,6 @@
from migen.fhdl.std import *
from migen.bus import wishbone
from migen.genlib.fsm import FSM
from migen.genlib.fsm import FSM, NextState
from migen.genlib.misc import split, displacer, chooser
from migen.genlib.record import Record, layout_len

Expand Down Expand Up @@ -71,61 +71,58 @@ def __init__(self, cachesize, lasmim):

# Control FSM
assert(lasmim.write_latency >= 1 and lasmim.read_latency >= 1)
fsm = FSM("IDLE", "TEST_HIT",
"EVICT_REQUEST", "EVICT_WAIT_DATA_ACK", "EVICT_DATA",
"REFILL_WRTAG", "REFILL_REQUEST", "REFILL_WAIT_DATA_ACK", "REFILL_DATA",
delayed_enters=[
("EVICT_DATAD", "EVICT_DATA", lasmim.write_latency-1),
("REFILL_DATAD", "REFILL_DATA", lasmim.read_latency-1)
])
fsm = FSM()
self.submodules += fsm

fsm.act(fsm.IDLE,
If(self.wishbone.cyc & self.wishbone.stb, fsm.next_state(fsm.TEST_HIT))
fsm.delayed_enter("EVICT_DATAD", "EVICT_DATA", lasmim.write_latency-1)
fsm.delayed_enter("REFILL_DATAD", "REFILL_DATA", lasmim.read_latency-1)

fsm.act("IDLE",
If(self.wishbone.cyc & self.wishbone.stb, NextState("TEST_HIT"))
)
fsm.act(fsm.TEST_HIT,
fsm.act("TEST_HIT",
If(tag_do.tag == adr_tag,
self.wishbone.ack.eq(1),
If(self.wishbone.we,
tag_di.dirty.eq(1),
tag_port.we.eq(1)
),
fsm.next_state(fsm.IDLE)
NextState("IDLE")
).Else(
If(tag_do.dirty,
fsm.next_state(fsm.EVICT_REQUEST)
NextState("EVICT_REQUEST")
).Else(
fsm.next_state(fsm.REFILL_WRTAG)
NextState("REFILL_WRTAG")
)
)
)

fsm.act(fsm.EVICT_REQUEST,
fsm.act("EVICT_REQUEST",
lasmim.stb.eq(1),
lasmim.we.eq(1),
If(lasmim.req_ack, fsm.next_state(fsm.EVICT_WAIT_DATA_ACK))
If(lasmim.req_ack, NextState("EVICT_WAIT_DATA_ACK"))
)
fsm.act(fsm.EVICT_WAIT_DATA_ACK,
If(lasmim.dat_ack, fsm.next_state(fsm.EVICT_DATAD))
fsm.act("EVICT_WAIT_DATA_ACK",
If(lasmim.dat_ack, NextState("EVICT_DATAD"))
)
fsm.act(fsm.EVICT_DATA,
fsm.act("EVICT_DATA",
write_to_lasmi.eq(1),
fsm.next_state(fsm.REFILL_WRTAG)
NextState("REFILL_WRTAG")
)

fsm.act(fsm.REFILL_WRTAG,
fsm.act("REFILL_WRTAG",
# Write the tag first to set the LASMI address
tag_port.we.eq(1),
fsm.next_state(fsm.REFILL_REQUEST)
NextState("REFILL_REQUEST")
)
fsm.act(fsm.REFILL_REQUEST,
fsm.act("REFILL_REQUEST",
lasmim.stb.eq(1),
If(lasmim.req_ack, fsm.next_state(fsm.REFILL_WAIT_DATA_ACK))
If(lasmim.req_ack, NextState("REFILL_WAIT_DATA_ACK"))
)
fsm.act(fsm.REFILL_WAIT_DATA_ACK,
If(lasmim.dat_ack, fsm.next_state(fsm.REFILL_DATAD))
fsm.act("REFILL_WAIT_DATA_ACK",
If(lasmim.dat_ack, NextState("REFILL_DATAD"))
)
fsm.act(fsm.REFILL_DATA,
fsm.act("REFILL_DATA",
write_from_lasmi.eq(1),
fsm.next_state(fsm.TEST_HIT)
NextState("TEST_HIT")
)
100 changes: 66 additions & 34 deletions migen/genlib/fsm.py
@@ -1,41 +1,73 @@
from collections import OrderedDict

from migen.fhdl.std import *
from migen.fhdl.module import FinalizeError
from migen.fhdl.visit import NodeTransformer

class FSM:
def __init__(self, *states, delayed_enters=[]):
nstates = len(states) + sum([d[2] for d in delayed_enters])

self._state = Signal(max=nstates)
self._next_state = Signal(max=nstates)
for n, state in enumerate(states):
setattr(self, state, n)
self.actions = [[] for i in range(len(states))]
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

for name, target, delay in delayed_enters:
target_state = getattr(self, target)
if delay:
name_state = len(self.actions)
setattr(self, name, name_state)
for i in range(delay-1):
self.actions.append([self.next_state(name_state+i+1)])
self.actions.append([self.next_state(target_state)])
else:
# alias
setattr(self, name, target_state)

def reset_state(self, state):
self._state.reset = state

def next_state(self, state):
return self._next_state.eq(state)

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):
def __init__(self):
self.actions = OrderedDict()
self.state_aliases = dict()
self.reset_state = None

def act(self, state, *statements):
if self.finalized:
raise FinalizeError
if state not in self.actions:
self.actions[state] = []
self.actions[state] += statements

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

def get_fragment(self):
cases = dict((s, a) for s, a in enumerate(self.actions) if a)
comb = [
self._next_state.eq(self._state),
Case(self._state, cases)
def do_finalize(self):
nstates = len(self.actions)

self.encoding = dict((s, n) for n, s in enumerate(self.actions.keys()))
self.state = Signal(max=nstates)
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),
Case(self.state, cases)
]
sync = [self._state.eq(self._next_state)]
return Fragment(comb, sync)
self.sync += self.state.eq(self.next_state)
10 changes: 5 additions & 5 deletions migen/pytholite/compiler.py
Expand Up @@ -166,10 +166,10 @@ def visit_if(self, sa, node):
states_f, exit_states_f = self.visit_block(node.orelse)
exit_states = exit_states_t + exit_states_f

test_state_stmt = If(test, AbstractNextState(states_t[0]))
test_state_stmt = If(test, id_next_state(states_t[0]))
test_state = [test_state_stmt]
if states_f:
test_state_stmt.Else(AbstractNextState(states_f[0]))
test_state_stmt.Else(id_next_state(states_f[0]))
else:
exit_states.append(test_state)

Expand All @@ -180,9 +180,9 @@ def visit_while(self, sa, node):
test = self.ec.visit_expr(node.test)
states_b, exit_states_b = self.visit_block(node.body)

test_state = [If(test, AbstractNextState(states_b[0]))]
test_state = [If(test, id_next_state(states_b[0]))]
for exit_state in exit_states_b:
exit_state.insert(0, AbstractNextState(test_state))
exit_state.insert(0, id_next_state(test_state))

sa.assemble([test_state] + states_b, [test_state])

Expand All @@ -199,7 +199,7 @@ def visit_for(self, sa, node):
self.symdict[target] = iteration
states_b, exit_states_b = self.visit_block(node.body)
for exit_state in last_exit_states:
exit_state.insert(0, AbstractNextState(states_b[0]))
exit_state.insert(0, id_next_state(states_b[0]))
last_exit_states = exit_states_b
states += states_b
del self.symdict[target]
Expand Down

0 comments on commit d0caa73

Please sign in to comment.