2. 状态机基础:状态、转移、事件、动作。摩尔型与米利型
好,我们正式开始聊状态机。说实话,很多工程师干了几年,状态机写得飞快,但真让他把基本概念讲清楚——嗯,反而有点含糊。我见过不少面试者,代码能跑,但问起「你的输出到底跟时钟沿什么关系」,就开始挠头。
今天这一章,我们就来把这些基础夯实。状态、转移、事件、动作,这四个词说白了就是状态机的全部。外加摩尔型和米利型这哥俩的区别,你搞明白了,后面写训练序列才不心虚。
2.1 状态机的四个核心要素
我个人习惯把状态机看作一个「有记忆的控制逻辑」。它不像组合逻辑那样,输入一变输出马上变。它得记住自己现在在哪,下一步要去哪。
- 状态(State):就是状态机当前所处的位置。好比一个人站在哪个路口。
- 事件(Event):触发转移的原因。可能是某个管脚电平跳变,也可能是内部计数器的溢出。
- 转移(Transition):满足条件后,从当前状态跳转到下一个状态。
- 动作(Action):进入某个状态时,或者转移过程中,执行的具体操作。比如拉高某个信号、置位一个标志位。
来看一段简单的状态机代码,我用 Python 写个示意,你们感受一下结构:
class SimpleFSM:
def __init__(self):
self.state = "IDLE"
def on_event(self, event):
if self.state == "IDLE":
if event == "START":
self.action_start()
self.state = "BUSY"
elif self.state == "BUSY":
if event == "DONE":
self.action_done()
self.state = "IDLE"
def action_start(self):
print("动作:启动处理")
def action_done(self):
print("动作:完成处理")
看到没有?状态、事件、转移、动作,四个东西各司其职。你别嫌简单,很多复杂状态机的大框架,底层就是这么个朴素的玩法。
2.2 摩尔型 vs 米利型
这是我每次讲课都重点对比的。你想想看,同样是状态机,为什么有的输出跟时钟沿同步,有的却对输入变化敏感?
| 对比项 | 摩尔型(Moore) | 米利型(Mealy) |
|---|---|---|
| 输出决定因素 | 只由当前状态决定 | 由当前状态 + 当前输入共同决定 |
| 输出时机 | 状态转移后,下一个时钟沿输出 | 输入变化的瞬间,输出可能立刻变化 |
| 安全性 | 更安全,输出稳定无毛刺 | 有风险,组合逻辑可能产生毛刺 |
| 状态数 | 通常需要更多状态来描述同一行为 | 可以用更少状态实现相同功能 |
| 典型场景 | 控制信号、寄存器写使能 | 高速数据传输、握手协议 |
2.3 两种模型的代码对比
我们写两个简单的例子,一个是摩尔型检测上升沿,一个是米利型检测上升沿。你一看就懂了。
摩尔型上升沿检测
# 摩尔型:输出只跟当前状态有关
# 状态 S0: 低电平
# 状态 S1: 高电平
def moore_rising_edge(clk, din):
if clk & 0x1: # 时钟上升沿
if state == "S0":
if din == 1:
state = "S1"
out = 1 # 进入S1时输出1
else:
out = 0
elif state == "S1":
if din == 0:
state = "S0"
out = 0 # 在S1时输出0
return out
米利型上升沿检测
# 米利型:输出由当前状态和输入共同决定
# 状态 S0: 低电平
# 状态 S1: 高电平
def mealy_rising_edge(clk, din):
if clk & 0x1: # 时钟上升沿
if state == "S0":
if din == 1:
state = "S1"
out = 1 # 转移中输出1
else:
out = 0
elif state == "S1":
if din == 0:
state = "S0"
out = 0
return out
看起来代码几乎一样对吧?关键区别在于:摩尔型是在进入 S1 这个状态时输出 1;米利型是在 S0 状态下收到 din=1 这个事件的 转移过程 中输出 1。你品,你细品。
一句话总结:
- 摩尔型:输出 = f(当前状态)
- 米利型:输出 = f(当前状态, 当前输入)
多了一个输入维度,反应更快,但代价是稳定性和复杂度。
2.4 本章知识体系总览
为了让你直观理解这些概念怎么串起来,我画了一张图。别小看这张图,它基本概括了状态机的全部玩法。
画完这张图,我自己又看了看,觉得该说的都在上面了。状态、转移、事件、动作,加上摩尔和米利的输出差异,一目了然。
2.5 实战选型建议
最后我分享点个人的经验。项目中到底选摩尔还是米利?
- 控制类逻辑(比如按键消抖、LED闪烁):我闭着眼睛用摩尔型。输出稳定,不容易出奇奇怪怪的bug。
- 数据通路(比如 FIFO 读写控制、DMA 传输):米利型有时更高效,因为可以用组合路径减少延时。但要做好时序约束和毛刺过滤。
好,这一章的内容就到这。记住:状态是骨架,事件是触发器,转移是路径,动作是行为。摩尔型输出只认状态,米利型输出还看输入。把这四个东西刻在脑子里,后面讲训练序列的时候,你自然能跟上。