mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
FSM: new API
This commit is contained in:
parent
b56cb3cefc
commit
d0caa738bd
8 changed files with 137 additions and 135 deletions
|
@ -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}))
|
||||
|
|
|
@ -47,18 +47,18 @@ class IntSequence(Module):
|
|||
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"))
|
||||
)
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -79,60 +79,58 @@ class WB2ASMI:
|
|||
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}) \
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -71,61 +71,58 @@ class WB2LASMI(Module):
|
|||
|
||||
# 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")
|
||||
)
|
||||
|
|
|
@ -1,41 +1,73 @@
|
|||
from migen.fhdl.std import *
|
||||
from collections import OrderedDict
|
||||
|
||||
class FSM:
|
||||
def __init__(self, *states, delayed_enters=[]):
|
||||
nstates = len(states) + sum([d[2] for d in delayed_enters])
|
||||
from migen.fhdl.std import *
|
||||
from migen.fhdl.module import FinalizeError
|
||||
from migen.fhdl.visit import NodeTransformer
|
||||
|
||||
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
|
||||
|
||||
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))]
|
||||
|
||||
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)
|
||||
|
|
|
@ -166,10 +166,10 @@ class _Compiler:
|
|||
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)
|
||||
|
||||
|
@ -180,9 +180,9 @@ class _Compiler:
|
|||
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])
|
||||
|
||||
|
@ -199,7 +199,7 @@ class _Compiler:
|
|||
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]
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
from migen.fhdl import visit as fhdl
|
||||
from migen.genlib.fsm import FSM
|
||||
from migen.genlib.fsm import FSM, NextState
|
||||
|
||||
class AbstractNextState:
|
||||
def __init__(self, target_state):
|
||||
self.target_state = target_state
|
||||
def id_next_state(l):
|
||||
return NextState(id(l))
|
||||
|
||||
# entry state is first state returned
|
||||
class StateAssembler:
|
||||
|
@ -14,37 +12,14 @@ class StateAssembler:
|
|||
def assemble(self, n_states, n_exit_states):
|
||||
self.states += n_states
|
||||
for exit_state in self.exit_states:
|
||||
exit_state.insert(0, AbstractNextState(n_states[0]))
|
||||
exit_state.insert(0, id_next_state(n_states[0]))
|
||||
self.exit_states = n_exit_states
|
||||
|
||||
def ret(self):
|
||||
return self.states, self.exit_states
|
||||
|
||||
# like list.index, but using "is" instead of comparison
|
||||
def _index_is(l, x):
|
||||
for i, e in enumerate(l):
|
||||
if e is x:
|
||||
return i
|
||||
|
||||
class _LowerAbstractNextState(fhdl.NodeTransformer):
|
||||
def __init__(self, fsm, states, stnames):
|
||||
self.fsm = fsm
|
||||
self.states = states
|
||||
self.stnames = stnames
|
||||
|
||||
def visit_unknown(self, node):
|
||||
if isinstance(node, AbstractNextState):
|
||||
index = _index_is(self.states, node.target_state)
|
||||
estate = getattr(self.fsm, self.stnames[index])
|
||||
return self.fsm.next_state(estate)
|
||||
else:
|
||||
return node
|
||||
|
||||
def implement_fsm(states):
|
||||
stnames = ["S" + str(i) for i in range(len(states))]
|
||||
fsm = FSM(*stnames)
|
||||
lans = _LowerAbstractNextState(fsm, states, stnames)
|
||||
for i, state in enumerate(states):
|
||||
actions = lans.visit(state)
|
||||
fsm.act(getattr(fsm, stnames[i]), *actions)
|
||||
fsm = FSM()
|
||||
for state in states:
|
||||
fsm.act(id(state), state)
|
||||
return fsm
|
||||
|
|
|
@ -50,7 +50,7 @@ def _gen_df_io(compiler, modelname, to_model, from_model):
|
|||
state += [reg.load(cexpr) for reg in target_regs]
|
||||
state += [
|
||||
ep.ack.eq(1),
|
||||
If(~ep.stb, AbstractNextState(state))
|
||||
If(~ep.stb, id_next_state(state))
|
||||
]
|
||||
return [state], [state]
|
||||
else:
|
||||
|
@ -67,7 +67,7 @@ def _gen_df_io(compiler, modelname, to_model, from_model):
|
|||
state.append(signal.eq(compiler.ec.visit_expr(value)))
|
||||
state += [
|
||||
ep.stb.eq(1),
|
||||
If(~ep.ack, AbstractNextState(state))
|
||||
If(~ep.ack, id_next_state(state))
|
||||
]
|
||||
return [state], [state]
|
||||
|
||||
|
@ -110,7 +110,7 @@ def _gen_wishbone_io(compiler, modelname, model, to_model, from_model, bus):
|
|||
for target_regs, expr in from_model:
|
||||
cexpr = ec.visit_expr(expr)
|
||||
state += [reg.load(cexpr) for reg in target_regs]
|
||||
state.append(If(~bus.ack, AbstractNextState(state)))
|
||||
state.append(If(~bus.ack, id_next_state(state)))
|
||||
return [state], [state]
|
||||
|
||||
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):
|
|||
return [s1], [s1]
|
||||
else:
|
||||
s2 = []
|
||||
s1.append(AbstractNextState(s2))
|
||||
s1.append(id_next_state(s2))
|
||||
ec = _BusReadExprCompiler(compiler.symdict, modelname, port.dat_r)
|
||||
for target_regs, expr in from_model:
|
||||
cexpr = ec.visit_expr(expr)
|
||||
|
|
Loading…
Reference in a new issue