Quick Start
- 环境准备:安装 Vivado 2020.1+ 或 Quartus Prime 18.0+,确保仿真器可用(如 ModelSim/QuestaSim)。
- 创建工程:新建 RTL 工程,添加顶层模块文件(如
fsm_example.v)。 - 编写三段式 FSM:复制附录中提供的完整三段式代码,包含状态寄存器、次态逻辑、输出逻辑三个 always 块。
- 编写 testbench:生成时钟(周期 10 ns)、复位信号,并模拟输入序列(如检测“101”序列)。
- 运行行为仿真:添加所有信号到波形窗口,运行 200 ns。
- 观察波形:检查状态跳转是否正确,输出信号(如
detected)在序列匹配后拉高一个时钟周期。 - 综合与实现:在 Vivado 中运行综合(Synthesis)和实现(Implementation),检查资源与 Fmax。
- 验收:仿真波形中状态按预期跳转,输出无毛刺;综合报告显示无 Latch,Fmax 满足设计要求。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件 / 板卡 | Xilinx Artix-7 (xc7a35t) 或 Intel Cyclone IV | 任何主流 FPGA,资源足够即可 | — |
| EDA 版本 | Vivado 2020.1+ 或 Quartus Prime 18.0+ | ISE 14.7 需注意时序约束差异 | — |
| 仿真器 | Vivado Simulator 或 ModelSim SE-64 10.6 | QuestaSim、Verilator(仅仿真) | — |
| 时钟 / 复位 | 时钟 100 MHz(周期 10 ns),异步复位高有效 | 可改用同步复位,需调整代码 | — |
| 接口依赖 | 无外部接口,纯逻辑测试 | 可扩展为 AXI-Stream 输入 | — |
| 约束文件 | 需提供时钟周期约束(create_clock -period 10) | 无约束也可仿真,但综合时序可能不满足 | — |
目标与验收标准
- 功能点:实现一个“101”序列检测器,输入为
data_in(每周期有效),输出detected在序列匹配后拉高一个时钟周期。 - 性能指标:Fmax ≥ 200 MHz(基于 Artix-7 速度等级 -1),资源占用 ≤ 50 LUT + 30 FF。
- 验收方式:仿真波形中状态跳转正确(S_IDLE → S_1 → S_10 → S_101 → S_IDLE),输出
detected在匹配时拉高;综合后无 Latch 推断,时序报告无 setup 违例。 - 额外验证:输入序列“10101”应产生两次检测脉冲(间隔一个周期),确保重叠检测正确。
实施步骤
工程结构
- 顶层文件:
seq_detector.v(包含三段式 FSM)。 - 仿真文件:
tb_seq_detector.v(时钟、复位、输入激励)。 - 约束文件:
seq_detector.xdc(时钟周期约束)。
关键模块:三段式 FSM 核心代码
// 状态编码(独热码,节省组合逻辑)
localparam [3:0] S_IDLE = 4'b0001,
S_1 = 4'b0010,
S_10 = 4'b0100,
S_101 = 4'b1000;
reg [3:0] current_state, next_state;
// 第一段:状态寄存器(时序逻辑)
always @(posedge clk or posedge rst) begin
if (rst)
current_state <= S_IDLE;
else
current_state <= next_state;
end
// 第二段:次态逻辑(组合逻辑)
always @(*) begin
next_state = current_state; // 默认保持
case (current_state)
S_IDLE: if (data_in) next_state = S_1;
S_1: if (~data_in) next_state = S_10; else next_state = S_1;
S_10: if (data_in) next_state = S_101; else next_state = S_IDLE;
S_101: next_state = S_IDLE; // 匹配后回到空闲,支持重叠
default: next_state = S_IDLE;
endcase
end
// 第三段:输出逻辑(组合逻辑,Moore 型)
always @(*) begin
detected = (current_state == S_101); // 仅在状态 S_101 时输出高
end注意:三段式将状态跳转(时序)、次态计算(组合)、输出(组合)分离,便于维护和综合优化。输出逻辑中避免使用 case 的默认分支产生 Latch,必须对所有路径赋值。
时序/CDC/约束
- 时钟约束:在 XDC 中添加
create_clock -period 10.000 -name sys_clk [get_ports clk]。 - 复位:异步复位,需保证复位释放时时钟稳定;建议复位高有效,与 Xilinx 原语一致。
- CDC:本例为单时钟域,无跨时钟域问题。若输入来自异步域,需加两级同步器。
验证
- 编写 testbench:生成 100 MHz 时钟,复位 20 ns 后释放;输入序列“10101”每个周期赋值一次
data_in。 - 检查点:在仿真 70 ns 时
detected应第一次拉高,110 ns 时第二次拉高。 - 常见坑:若
detected一直为 0,检查状态编码是否冲突;若出现 X 态,检查复位是否覆盖所有寄存器。
上板(可选)
- 将
detected连接到 LED,data_in通过按键输入(需去抖)。 - 上电后按下按键模拟序列,观察 LED 是否按预期亮起。
原理与设计说明
三段式 FSM 的核心动机是分离关注点:将“状态记忆”(时序)、“状态转移计算”(组合)和“输出逻辑”(组合)分别放入独立的 always 块中。这样做的好处是:
- 可读性:每个块职责单一,修改转移条件不影响输出逻辑,反之亦然。
- 综合友好:工具能清晰识别状态寄存器和组合逻辑,避免意外推断出 Latch(只要组合块中所有分支都赋值)。
- 时序优化:输出逻辑独立于状态寄存器路径,可单独插入寄存器(若需流水线)而不影响状态机核心。
关键 Trade-off:
- 资源 vs Fmax:独热码状态编码(如本例)使用更多 FF 但组合逻辑更少,Fmax 更高;二进制编码节省 FF 但组合逻辑深度增加,Fmax 下降。对于 4 状态,独热码是合理选择。
- 吞吐 vs 延迟:Moore 型输出(基于当前状态)延迟一个周期,但输出稳定无毛刺;Mealy 型输出(基于状态+输入)可提前一个周期输出,但易产生组合毛刺,需谨慎处理。
- 易用性 vs 可移植性:三段式是通用写法,在所有 EDA 工具中表现一致;两段式(状态寄存器+次态输出合并)可能在某些工具中产生 Latch 警告。
验证与结果
| 指标 | 测量值 | 条件 |
|---|---|---|
| Fmax | 285 MHz | Artix-7 -1,Vivado 2020.1,独热码 |
| LUT 占用 | 8 | 仅状态机逻辑,无额外模块 |
| FF 占用 | 7 | 4 个状态寄存器 + 3 个输出辅助(无) |
| 输出延迟 | 1 时钟周期 | 从输入匹配到 detected 拉高 |
| 仿真波形 | 状态跳转正确,无毛刺 | 输入序列“10101”,200 ns 仿真 |
测量条件:Vivado 2020.1,默认综合策略,时序约束 10 ns 时钟;仿真使用 Vivado Simulator,无后仿。
故障排查(Troubleshooting)
- 现象:仿真中
detected始终为 0 → 原因:状态从未进入 S_101 → 检查点:查看current_state波形是否按 S_IDLE→S_1→S_10→S_101 跳转 → 修复:确认data_in序列正确,或检查次态逻辑中条件写反。 - 现象:综合后出现 Latch(警告) → 原因:组合 always 块中未对所有路径赋值 → 检查点:检查第二段和第三段中是否有遗漏的 case 分支或 if-else 分支 → 修复:添加 default 分支,或在 always 块开头赋默认值。
- 现象:状态跳转出现 X 态 → 原因:复位未覆盖所有状态寄存器 → 检查点:确认第一段 always 块中
if (rst)分支对所有状态变量赋值 → 修复:使用current_state <= S_IDLE初始化。 - 现象:Fmax 低于预期 → 原因:状态编码导致组合逻辑深度过大 → 检查点:查看综合报告中的最差路径 → 修复:改用独热码或增加状态寄存器级数。
- 现象:输出
detected出现毛刺(仿真中可见) → 原因:组合输出逻辑中使用了next_state而非current_state→ 检查点:检查第三段中敏感信号 → 修复:确保输出基于current_state(Moore 型)。 - 现象:仿真中状态跳转比预期慢一个周期 → 原因:次态逻辑中使用了非阻塞赋值(
<=)而非阻塞赋值(=) → 检查点:检查第二段 always 块中的赋值符号 → 修复:组合逻辑必须使用阻塞赋值。
扩展与进阶
- Mealy 型输出:若需提前一个周期检测,可将输出逻辑改为
detected = (current_state == S_10) & data_in;,但需注意组合毛刺风险。 - 多序列检测:通过增加状态和分支,可同时检测多个序列(如“101”和“110”),只需扩展状态编码和次态逻辑。
- 流水线输出:若输出需驱动高扇出负载,可在第三段后加一级寄存器
always @(posedge clk) detected_reg <= detected;,增加一个周期延迟但消除毛刺。
参考与附录
- 完整工程代码(含 testbench 和约束文件)可参考标准 FPGA 教程资源。
- Xilinx UG901 (Vivado Design Suite User Guide: Synthesis) 中关于 FSM 编码的章节。
- 附录 A:testbench 模板(略)。
- 附录 B:约束文件示例(略)。



