Yosys FSM 状态机未识别:fsm_extract 认不出的两类写法
更新时间:2026-10-31。FAQ 速查页,属于 EDA 报错排查系列。Yosys FSM 状态机未识别,指的是
fsm_detect没把寄存器转换成专用 FSM 单元,因而后续fsm_extract没有状态表可提取;它与语法错误、多驱动或综合中断不是一回事。其他开源工具的诊断入口见 开源 EDA 报错排查导航。
首屏答案:标准的状态常量、case 转移和明确复位可以被自动提取;把原始状态寄存器直接接到模块端口,或让下一状态依赖 state + 1 这类算术结果,可能不会进入专用 FSM 流程。后两种电路仍可由寄存器、加法器和多路选择器正常实现。判断设计是否可用,要看完整综合是否成功以及网表功能,而不是只看有没有 Extracting FSM。
本文要回答的问题
fsm_detect与fsm_extract分别负责什么?- 为什么状态调试端口会阻止自动提取?
- 算术递推与常量状态转移有什么识别差异?
- 没有提取日志时,怎样确认综合输出仍然可用?
一、未提取只是走了另一条综合路径
有限状态机(Finite State Machine,FSM)提取是一项优化:工具先寻找满足模式的状态寄存器,再收集状态编码、输入条件和转移关系,之后才可能重编码。它并不是 Verilog 详细化的必需步骤。
图中蓝色是识别检查,绿色是专用 FSM 路径,橙色是普通逻辑路径,两条路最后都到网表。橙色分支没有错误节点,因为“不能或不适合重编码”本身不表示电路无法综合。真正的失败应以 ERROR、非零退出码或检查不通过为准。
还要注意,空荡的 FSM_EXTRACT 小节可能有两种情况:候选寄存器被明确拒绝,或者一开始就没有形成候选。前者通常会给出 Not marking ... as FSM state register 及原因;后者可能只留下各处理阶段的标题。
二、标准 case 写法能建立状态转移表
实验中的标准模块用 IDLE、WORK、DONE 三个常量描述状态。时序过程只保存 next_state,组合过程给出完整默认赋值,并为非法编码回到 IDLE。
always @(posedge clk) begin
if (rst)
state <= IDLE;
else
state <= next_state;
end
always @* begin
next_state = state;
case (state)
IDLE: if (start) next_state = WORK;
WORK: if (finish) next_state = DONE;
DONE: next_state = IDLE;
default: next_state = IDLE;
endcase
end这种写法的下一状态来自状态常量或保持原值,工具能把选择逻辑整理成有限转移表。Yosys 0.33 的原始记录明确找到了状态寄存器,并列出复位状态和三个状态编码。
next_state = state 与 default 在这里也有实际硬件意义:前者让未命中的条件保持原状态,后者处理没有列出的编码。若组合过程在某些分支完全不给 next_state 赋值,可能引入锁存器,那已经是另一类结构问题。排查识别失败时不要把所有问题都归因于 case 写法,应先确认过程本身生成的是预期的寄存器和多路选择逻辑。
Found FSM state register standard.state.
Extracting FSM `\state' from module `\standard'.
found reset state: 2'00 (guessed from mux tree)
found state code: 2'00
found state code: 2'10
found state code: 2'01后续 fsm_recode 把自动编码落成三位独热码,即每态一个有效状态位。源码中的两位寄存器变成网表中的三位实现并不表示功能被改坏,而是专用提取带来的编码选择;只有确实需要固定编码时,才应另外约束编码策略。
三、状态外露与算术递推都没有进入提取
第二份设计只增加一个调试端口:assign debug_state = state;,其余 case 逻辑完全不变。终端卡保留了工具给出的直接原因。
Register is connected to module port 是关键。原始状态一旦成为模块接口,重编码就可能改变外部可观察编码;自动流程因此不把它当作适合重编码的封闭状态。若只需要知道当前是否空闲,可输出 state == IDLE 这类派生状态,而不是暴露整个原始寄存器。标准组已有 ready、done 两个派生输出,仍能正常提取。
第三组把状态推进写成一个时序过程:
else if ((state == 2'd0 && start) ||
(state == 2'd1 && finish))
state <= state + 1'b1;功能上,它与前两组走相同的 IDLE → WORK → DONE → IDLE 路径,但下一状态包含加法器输出。该组的 FSM_DETECT 下没有 Found FSM state register,FSM_EXTRACT 也为空。它随后仍保留 $add 对应的算术逻辑,并按普通寄存器电路继续处理。
| 写法 | 本次识别结果 | 综合退出码 | 网表复跑 |
|---|---|---|---|
状态常量加完整 case | 找到并提取,自动重编码为独热码 | 0 | PASS |
相同的 case,原始 state 接输出端口 | 明确拒绝标记为 FSM | 0 | PASS |
下一状态使用 state + 1'b1 | 未形成可提取的 FSM | 0 | PASS |
这张表不能把三种单元数量直接当成优化优劣:它们只证明走了不同实现路径。是否需要专用提取,应结合编码要求、时序和资源结果判断;没有自动提取,不等于逻辑一定更差。
也不要为了让状态“看起来封闭”而随手删除调试端口。若其他模块或验证环境依赖 debug_state 的二位编码,改成独热码会直接破坏约定。此时保留普通逻辑路径是合理选择。反过来,若端口只是临时观察内部进度,派生一个 debug_idle 或 debug_done 往往比公开整个状态字更稳定,既减少接口承诺,也给实现保留重编码空间。
四、综合输出要复跑,不能只看识别日志
三组实验都执行完整 synth -top <模块名>,写出独立 Verilog 网表,再用 Icarus Verilog 12.0 编译。共享测试台依次检查复位后的空闲、start 后工作、finish 后完成以及返回空闲四个阶段,任何输出不符都会触发 $fatal。
synthesis_exit=0
PASS synthesized netlist
simulation_exit=0
synthesis_exit=0
PASS synthesized netlist
simulation_exit=0
synthesis_exit=0
PASS synthesized netlist
simulation_exit=0三行 PASS 分别对应标准、状态外露、算术递推三组,证明本次输入序列下综合网表可用。这是网表功能复跑,不是覆盖所有输入序列的形式等价证明;工程里仍应保留完整回归。它足以排除“没有 Extracting FSM,所以综合产物必然无效”这个误判。
遇到 Yosys FSM 状态机未识别时,可按下面顺序处理:
- 先确认
synth的退出码和末尾网表是否生成,不把优化选择当报错。 - 在
FSM_DETECT附近找Not marking,区分端口外露与没有候选两种情况。 - 若确实需要自动重编码,保持原始状态内部化,用常量表达有限转移,并提供明确复位。
- 若状态编码是对外协议或调试接口的一部分,不应为了出现提取日志而破坏接口约定。
- 无论是否提取,都对生成的网表做功能复跑,不能仅比较日志行数。
实验环境
- 机器/系统:MacBook Pro / macOS 26.5.2,Docker 容器
geek-eda:0.2 - 工具版本:Yosys 0.33 (git sha1 2584903a060);Icarus Verilog version 12.0 (stable)
- 日期:2026-10-31
- 完整命令:在代码目录执行
bash run.sh。脚本对standard、exported、arithmetic分别运行docker run --rm -v "$PWD:/work" -w /work geek-eda:0.2 yosys ...,执行read_verilog、synth -top、stat和write_verilog;随后用同一容器中的iverilog与vvp编译、运行各组综合网表。
代码获取
三份设计、共享测试台与完整运行脚本位于公开仓 https://github.com/derekzhuo/geek-doc.cn/tree/main/experiments/ts-yosys-fsm-extract-fail。准备同名容器镜像后进入该目录执行 bash run.sh,即可复现识别日志和三组网表复跑。
这一篇在知识体系里的位置
本篇处理 Yosys 综合优化阶段的识别边界,不是 Verilog 编译错误。需要回到工具类型与错误阶段定位时,从 开源 EDA 报错排查导航 进入;另一个“命令成功但结果未按预期变化”的例子是 iverilog -P 参数覆盖。
总结
Yosys FSM 状态机未识别只表示没有进入专用提取和重编码路径。Yosys 0.33 实测中,标准常量 case 状态机被提取;原始状态接模块端口会被明确拒绝,state + 1 的递推写法也未形成 FSM,但三组综合均退出 0,生成网表都通过同一功能复跑。先查 FSM_DETECT 的原因,再按接口与编码需求决定是否改写,最后用网表回归确认结果。