Verilog 状态机编码风格对综合面积影响的设计指南与实测分析(2026年Q2)

FPGA小白
文章2026-06-29
34 0 0 0

Quick Start

  • 下载并安装 Vivado 2026.1(或更高版本),确保已添加目标器件库(示例:xc7a35tcsg324-1)。
  • 创建一个空工程,选择 RTL 项目,添加本文提供的三个状态机 RTL 文件(one-hot、binary、gray)。
  • 为每个状态机模块分别创建顶层 wrapper,例化并连接时钟(100 MHz)、复位(异步高有效)及 8-bit 输出总线。
  • 运行综合(synth_1),保持默认策略(Vivado Synthesis Defaults)。
  • 综合完成后,打开“Report Utilization”,分别记录每个状态机模块的 LUT、FF、Slice 数量。
  • 对比三个模块的资源占用,观察 one-hot 编码是否比 binary 消耗更多 LUT,但 FF 更少(或相反)。
  • (可选)运行实现(impl_1)并打开“Report Timing Summary”,记录各状态机的 Fmax,验证 one-hot 是否通常获得更高 Fmax。

前置条件与环境

项目推荐值说明替代方案
器件/板卡Xilinx Artix-7 (xc7a35tcsg324-1)主流低成本 FPGA,资源有限,面积差异明显Intel Cyclone V / Lattice ECP5(需调整约束)
EDA 版本Vivado 2026.12026年Q2最新稳定版,综合策略稳定Vivado 2025.x / 2024.x(结果趋势一致)
仿真器Vivado Simulator (xsim)用于功能验证,确保状态机行为正确ModelSim / Questa / VCS
时钟/复位时钟 100 MHz,异步高有效复位标准同步设计,便于对比时序其它频率(50-200 MHz 均可)
接口依赖8-bit 输出总线,无外部输入状态机内部自循环,避免 IO 影响面积可增加输入信号(面积变化趋势不变)
约束文件create_clock -period 10.000 [get_ports clk]仅时钟约束,无需 I/O 约束可添加 set_false_path 对复位

目标与验收标准

  • 功能点:三个状态机模块实现相同的 8 状态序列(S0→S1→…→S7→S0),每个状态输出唯一 8-bit 值。
  • 面积指标:记录各模块的 LUT 数、FF 数、Slice 数。预期 one-hot 比 binary 多 20%-50% LUT,但 FF 少 30%-50%(因 one-hot 需更多组合逻辑解码,但每个状态只需 1 个 FF)。
  • 性能指标:记录各模块的 Fmax(WNS 负值越小越好)。预期 one-hot 比 binary 高 10%-30%(因组合逻辑深度更浅)。
  • 验收方式:打开 Vivado 的“Report Utilization”和“Report Timing Summary”,截图保存;仿真波形确认状态跳转正确。

实施步骤

1. 工程结构与 RTL 编写

创建三个独立的 Verilog 模块:fsm_onehot、fsm_binary、fsm_gray。每个模块包含 8 个状态,状态跳转逻辑相同(循环移位),输出为当前状态对应的 8-bit 常量。

// fsm_onehot.v
module fsm_onehot (
    input wire clk,
    input wire rst_n,
    output reg [7:0] out
);

    localparam S0 = 8'b0000_0001;
    localparam S1 = 8'b0000_0010;
    localparam S2 = 8'b0000_0100;
    localparam S3 = 8'b0000_1000;
    localparam S4 = 8'b0001_0000;
    localparam S5 = 8'b0010_0000;
    localparam S6 = 8'b0100_0000;
    localparam S7 = 8'b1000_0000;

    reg [7:0] state, next_state;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            state <= S0;
        else
            state <= next_state;
    end

    always @(*) begin
        case (state)
            S0: next_state = S1;
            S1: next_state = S2;
            S2: next_state = S3;
            S3: next_state = S4;
            S4: next_state = S5;
            S5: next_state = S6;
            S6: next_state = S7;
            S7: next_state = S0;
            default: next_state = S0;
        endcase
    end

    always @(*) begin
        case (state)
            S0: out = 8'h01;
            S1: out = 8'h02;
            S2: out = 8'h04;
            S3: out = 8'h08;
            S4: out = 8'h10;
            S5: out = 8'h20;
            S6: out = 8'h40;
            S7: out = 8'h80;
            default: out = 8'h00;
        endcase
    end

endmodule

逐行说明(fsm_onehot)

  • 第 1-5 行:模块声明,时钟 clk、复位 rst_n(低有效)、8-bit 输出 out。
  • 第 7-14 行:使用 localparam 定义 8 个 one-hot 编码状态,每个状态只有 1 bit 为 1,其余为 0。这是 one-hot 的核心特征。
  • 第 16 行:声明 state(当前状态)和 next_state(次态)为 8-bit 寄存器。
  • 第 18-22 行:时序逻辑,在时钟上升沿或复位下降沿更新 state。复位时赋初值 S0。
  • 第 24-33 行:组合逻辑,根据当前 state 计算 next_state。case 语句覆盖所有状态,default 防止锁存。
  • 第 35-44 行:组合逻辑,根据当前 state 输出对应数值。输出与状态一一对应,无额外编码。
// fsm_binary.v
module fsm_binary (
    input wire clk,
    input wire rst_n,
    output reg [7:0] out
);

    localparam S0 = 3'd0;
    localparam S1 = 3'd1;
    localparam S2 = 3'd2;
    localparam S3 = 3'd3;
    localparam S4 = 3'd4;
    localparam S5 = 3'd5;
    localparam S6 = 3'd6;
    localparam S7 = 3'd7;

    reg [2:0] state, next_state;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            state <= S0;
        else
            state <= next_state;
    end

    always @(*) begin
        case (state)
            S0: next_state = S1;
            S1: next_state = S2;
            S2: next_state = S3;
            S3: next_state = S4;
            S4: next_state = S5;
            S5: next_state = S6;
            S6: next_state = S7;
            S7: next_state = S0;
            default: next_state = S0;
        endcase
    end

    always @(*) begin
        case (state)
            S0: out = 8'h01;
            S1: out = 8'h02;
            S2: out = 8'h04;
            S3: out = 8'h08;
            S4: out = 8'h10;
            S5: out = 8'h20;
            S6: out = 8'h40;
            S7: out = 8'h80;
            default: out = 8'h00;
        endcase
    end

endmodule

逐行说明(fsm_binary)

  • 第 1-5 行:与 one-hot 相同,模块接口一致。
  • 第 7-14 行:使用 3-bit 二进制编码(0-7),状态寄存器宽度仅 3 bit,比 one-hot 的 8 bit 少。
  • 第 16 行:state 和 next_state 为 3-bit 寄存器,节省 FF 资源。
  • 第 18-22 行:时序逻辑,与 one-hot 相同。
  • 第 24-33 行:组合逻辑,case 分支数相同(8 个),但综合工具需将 3-bit 值解码为跳转逻辑,组合逻辑深度通常比 one-hot 深。
  • 第 35-44 行:输出逻辑,与 one-hot 相同(输出 8-bit 独热码,仅用于对比,实际应用中输出可能不同)。
// fsm_gray.v
module fsm_gray (
    input wire clk,
    input wire rst_n,
    output reg [7:0] out
);

    localparam S0 = 3'b000;
    localparam S1 = 3'b001;
    localparam S2 = 3'b011;
    localparam S3 = 3'b010;
    localparam S4 = 3'b110;
    localparam S5 = 3'b111;
    localparam S6 = 3'b101;
    localparam S7 = 3'b100;

    reg [2:0] state, next_state;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            state <= S0;
        else
            state <= next_state;
    end

    always @(*) begin
        case (state)
            S0: next_state = S1;
            S1: next_state = S2;
            S2: next_state = S3;
            S3: next_state = S4;
            S4: next_state = S5;
            S5: next_state = S6;
            S6: next_state = S7;
            S7: next_state = S0;
            default: next_state = S0;
        endcase
    end

    always @(*) begin
        case (state)
            S0: out = 8'h01;
            S1: out = 8'h02;
            S2: out = 8'h04;
            S3: out = 8'h08;
            S4: out = 8'h10;
            S5: out = 8'h20;
            S6: out = 8'h40;
            S7: out = 8'h80;
            default: out = 8'h00;
        endcase
    end

endmodule

逐行说明(fsm_gray)

  • 第 1-5 行:模块接口与 one-hot、binary 一致。
  • 第 7-14 行:使用格雷码(相邻状态仅 1 bit 变化),宽度仍为 3 bit。
  • 第 16 行:3-bit 寄存器,与 binary 相同。
  • 第 18-22 行:时序逻辑,复位到 S0(格雷码 000)。
  • 第 24-33 行:跳转逻辑,格雷码顺序不是自然二进制顺序,但综合工具会优化为等效逻辑。
  • 第 35-44 行:输出逻辑,与 one-hot 相同。

2. 综合与实现

在 Vivado 中分别对三个模块运行综合。注意:为避免工具优化掉未使用的逻辑,确保每个模块的顶层有输出端口连接到顶层端口(例如 out 连接到 LED 或 GPIO)。

3. 面积与性能报告解读

打开“Report Utilization”,关注以下指标:

  • Slice LUTs:组合逻辑消耗。
  • Slice Registers:触发器消耗。
  • Slice:整体逻辑单元。

打开“Report Timing Summary”,记录 WNS(最差负时序裕量),计算 Fmax = 1 / (Tclk – WNS)。

常见坑与排查

  • 坑 1:综合工具自动优化编码。Vivado 默认会尝试将状态机重新编码为 one-hot 或 binary。为公平对比,需在综合设置中关闭自动 FSM 编码:set_property STEPS.SYNTH_DESIGN.ARGS.FSM_EXTRACTION off [get_runs synth_1](或在 GUI 中取消“FSM Extraction”)。
  • 坑 2:输出逻辑影响面积。本文输出逻辑相同,但若输出编码不同,面积差异可能被掩盖。建议保持输出逻辑一致。

原理与设计说明

为什么 one-hot 消耗更多 LUT? one-hot 状态机中,每个状态由独立的 FF 表示,跳转逻辑只需检查当前状态是否激活(1 bit 比较),组合逻辑深度浅。但状态数量多时,case 语句的分支数量不变,但每个分支的条件更简单(仅需判断 1 bit)。然而,综合工具会将 one-hot 编码的 case 语句实现为大量 AND-OR 逻辑,导致 LUT 数量增多。例如,8 状态 one-hot 需要 8 个 FF,但次态逻辑需要将 8 个输入(当前状态位)映射到 8 个输出(次态位),每个输出是一个 8 输入 OR 树,消耗较多 LUT。

为什么 binary 消耗更少 LUT? binary 编码仅需 log2(N) 个 FF,次态逻辑只需对 3-bit 值进行加 1 运算(或 case 解码),组合逻辑可被综合工具优化为简单的加法器或查找表,LUT 数量较少。但加法器链可能较长,导致 Fmax 降低。

格雷码的权衡:格雷码在跨时钟域场景中优势明显(相邻状态仅 1 bit 变化,降低亚稳态概率),但在单时钟域中,其面积和 Fmax 介于 binary 和 one-hot 之间。格雷码的跳转逻辑需要将 3-bit 值映射到下一个格雷码,综合工具通常实现为 ROM 或 LUT 树,面积略大于 binary。

关键 trade-off 总结:

编码风格FF 数LUT 数Fmax适用场景
One-hotN高高高速、大状态数(>16)
Binarylog2(N)低中资源受限、状态数少
Graylog2(N)中中跨时钟域、异步 FIFO

验证与结果

以下数据基于 Vivado 2026.1,器件 xc7a35tcsg324-1,关闭 FSM 自动提取,综合策略默认。结果以实际工程为准,此处为示例值。

模块Slice LUTsSlice RegistersSliceFmax (MHz)
fsm_onehot32812450
fsm_binary1837380
fsm_gray2239410

测量条件:时钟约束 10 ns(100 MHz),实际 Fmax 通过 WNS 换算。仿真验证所有模块在 100 MHz 下功能正确,状态跳转顺序一致。

故障排查(Troubleshooting)

  • 现象:综合后面积差异很小(<10%)。

    原因:Vivado 自动 FSM 提取未关闭,工具将三种编码统一优化为同一种实现。

    解决:按照“常见坑与排查”中的方法关闭 FSM 自动提取。

  • 现象:综合后 Fmax 差异不明显。

    原因:时钟频率较低(如 100 MHz),未达到状态机的极限频率。可尝试提高时钟频率(如 500 MHz)以暴露时序差异。

  • 现象:仿真时状态跳转错误。

    原因:复位极性或状态编码定义错误。检查 rst_n 是否为低有效,以及 localparam 值是否与预期一致。

扩展与进阶

本指南仅针对 8 状态简单状态机。实际应用中,状态数可能达到数十甚至上百,此时 one-hot 的面积劣势会更加明显(FF 数线性增长),但 Fmax 优势也更突出。建议在项目初期根据状态数和时序要求进行预评估,选择最合适的编码风格。此外,现代综合工具(如 Vivado)支持自动 FSM 编码选择,但理解底层原理有助于在关键路径上手动干预。

参考

  • Vivado Design Suite User Guide: Synthesis (UG901)
  • Xilinx WP231: HDL Coding Practices to Accelerate Design Performance
  • Clifford E. Cummings, “State Machine Coding Styles for Synthesis”, SNUG 2002

附录:完整 RTL 代码

本文所有 RTL 代码已在实施步骤中完整给出,可直接复制使用。如需顶层 wrapper 示例,请参考以下代码:

module top_onehot (
    input wire clk,
    input wire rst_n,
    output wire [7:0] led
);
    fsm_onehot u_fsm (
        .clk(clk),
        .rst_n(rst_n),
        .out(led)
    );
endmodule

类似地,可创建 top_binary 和 top_gray 模块。

分类
技术分享
标签
Verilog状态机编码风格
浏览 34收藏 0赞 0评论 0
分享:

相关推荐

同频道 · 相近分类

暂无相关推荐

作者

FPGA小白查看主页

同分类阅读

文章

延伸阅读与实操

  • 文章 + 课程联动深度文章常对应体系课章节,可一键选课。
  • 学习产出可参考笔记与作业案例在学习产出广场持续更新。

探索全站