FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
登录
首页-技术文章/快讯-技术分享-正文

Verilog 状态机编码方式对比与综合面积优化设计指南

FPGA小白FPGA小白
技术分享
1天前
0
0
13

Quick Start

  • 安装 Vivado 2024.2 或更高版本(或 Quartus Prime Pro 24.3+),新建工程并选择目标器件(如 XC7A35T)。
  • 编写一个简单的 4 状态 Moore 状态机(IDLE → S1 → S2 → S3 → IDLE),输出仅与当前状态相关。
  • 分别用二进制编码(Binary)、格雷编码(Gray)、独热码(One-hot)实现同一状态机,保存为三个独立模块。
  • 编写一个顶层模块,实例化三个状态机,并连接相同的输入激励(时钟、复位、输入信号)。
  • 运行综合(Synthesis),查看 Vivado 的“Schematic”视图,确认状态机被推断为 FSM 而非随机逻辑。
  • 打开综合报告(Report Utilization / Report Power),记录每种编码方式的 LUT、FF、Slice 数量与 Fmax。
  • 运行行为仿真(Behavioral Simulation),验证三种编码方式的状态跳转和输出波形一致。
  • 对比资源与 Fmax 数据,确认二进制编码面积最小、独热码 Fmax 最高、格雷码在面积与速度间折中。

前置条件与环境

项目推荐值说明替代方案
器件/板卡Xilinx Artix-7 XC7A35T典型低成本 FPGA,LUT 资源有限,适合面积对比Intel Cyclone 10 LP / Lattice iCE40
EDA 版本Vivado 2024.2支持 FSM 推断与编码属性(FSM_ENCODING)Quartus Prime Pro 24.3+ / Synplify Premier
仿真器Vivado Simulator 或 ModelSim SE-64 2024.1用于功能仿真验证状态机行为Verilator(仅仿真)
时钟/复位50 MHz 时钟,异步低电平复位典型频率,复位极性可调100 MHz / 同步复位
接口依赖无外部接口,纯内部逻辑状态机输入为 2-bit 信号,输出为 3-bit 编码可根据需要增加握手信号
约束文件create_clock -period 20.000 [get_ports clk]50 MHz 时钟约束,用于时序分析根据实际时钟频率调整

目标与验收标准

  • 功能点:三种编码方式的状态机在相同输入激励下,状态跳转与输出完全一致(通过仿真波形比对)。
  • 性能指标:二进制编码面积最小(LUT+FF 总数 ≤ 独热码的 70%),独热码 Fmax 最高(比二进制高 20% 以上),格雷码居中。
  • 资源验收:综合后报告显示 LUT 使用量:二进制 < 格雷 < 独热;FF 使用量:独热码等于状态数,二进制等于 log2(状态数)。
  • Fmax 验收:独热码 Fmax ≥ 二进制 Fmax × 1.2(示例值,以实际综合结果为准)。
  • 波形验收:仿真波形中状态寄存器(state_reg)的值在三种编码下不同,但状态跳转时序对齐。

实施步骤

工程结构

  • 建议目录结构:fsm_comparison/rtl/ 下放三个状态机模块(fsm_binary.v, fsm_gray.v, fsm_onehot.v)和顶层 top_fsm.v
  • 仿真目录 fsm_comparison/sim/ 下放 testbench tb_fsm.v
  • 约束目录 fsm_comparison/constr/ 下放 top_fsm.xdc

关键模块:二进制编码状态机

module fsm_binary (
    input wire clk,
    input wire rst_n,
    input wire [1:0] in,
    output reg [2:0] out
);

    localparam IDLE = 2'b00,
               S1   = 2'b01,
               S2   = 2'b10,
               S3   = 2'b11;

    reg [1:0] state, next_state;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            state &lt;= IDLE;
        else
            state &lt;= next_state;
    end

    always @(*) begin
        next_state = state;
        case (state)
            IDLE: if (in == 2'b01) next_state = S1;
            S1:   if (in == 2'b10) next_state = S2;
            S2:   if (in == 2'b11) next_state = S3;
            S3:   next_state = IDLE;
            default: next_state = IDLE;
        endcase
    end

    always @(*) begin
        case (state)
            IDLE: out = 3'b001;
            S1:   out = 3'b010;
            S2:   out = 3'b100;
            S3:   out = 3'b001;
            default: out = 3'b000;
        endcase
    end

endmodule

逐行说明

  • 第 1–6 行:模块端口声明。clk 和 rst_n 是时钟与复位,in 是 2-bit 输入,out 是 3-bit 输出。
  • 第 8–11 行:使用 localparam 定义状态编码,二进制编码用 2-bit 表示 4 个状态,面积最小。
  • 第 13 行:声明状态寄存器 state 和次态组合逻辑 next_state,均为 2-bit。
  • 第 15–19 行:时序逻辑,在时钟上升沿或复位下降沿更新 state。复位时进入 IDLE。
  • 第 21–28 行:组合逻辑描述次态转移。使用 case 语句,每个状态根据 in 跳转到下一状态。default 子句确保安全。
  • 第 30–37 行:组合逻辑输出,仅与 state 相关(Moore 型)。每个状态对应一个 3-bit 独热输出。

关键模块:格雷编码状态机

module fsm_gray (
    input wire clk,
    input wire rst_n,
    input wire [1:0] in,
    output reg [2:0] out
);

    localparam IDLE = 2'b00,
               S1   = 2'b01,
               S2   = 2'b11,
               S3   = 2'b10;

    reg [1:0] state, next_state;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            state &lt;= IDLE;
        else
            state &lt;= next_state;
    end

    always @(*) begin
        next_state = state;
        case (state)
            IDLE: if (in == 2'b01) next_state = S1;
            S1:   if (in == 2'b10) next_state = S2;
            S2:   if (in == 2'b11) next_state = S3;
            S3:   next_state = IDLE;
            default: next_state = IDLE;
        endcase
    end

    always @(*) begin
        case (state)
            IDLE: out = 3'b001;
            S1:   out = 3'b010;
            S2:   out = 3'b100;
            S3:   out = 3'b001;
            default: out = 3'b000;
        endcase
    end

endmodule

逐行说明

  • 第 8–11 行:格雷编码,相邻状态只有 1-bit 变化(00→01→11→10),减少组合逻辑毛刺,但面积略大于二进制。
  • 第 15–19 行:时序逻辑与二进制版本结构相同,只是状态编码值不同。注意 S2 编码为 2'b11,S3 为 2'b10,确保相邻跳转仅变 1 位。
  • 第 21–28 行:次态转移逻辑与二进制版本一致,仅状态编码值不同。
  • 第 30–37 行:输出逻辑与二进制版本一致,仅状态编码值不同。

关键模块:独热码状态机

module fsm_onehot (
    input wire clk,
    input wire rst_n,
    input wire [1:0] in,
    output reg [2:0] out
);

    localparam IDLE = 4'b0001,
               S1   = 4'b0010,
               S2   = 4'b0100,
               S3   = 4'b1000;

    reg [3:0] state, next_state;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            state &lt;= IDLE;
        else
            state &lt;= next_state;
    end

    always @(*) begin
        next_state = 4'b0000;
        case (1'b1)
            state[0]: if (in == 2'b01) next_state[1] = 1'b1;
            state[1]: if (in == 2'b10) next_state[2] = 1'b1;
            state[2]: if (in == 2'b11) next_state[3] = 1'b1;
            state[3]: next_state[0] = 1'b1;
            default: next_state[0] = 1'b1;
        endcase
    end

    always @(*) begin
        case (1'b1)
            state[0]: out = 3'b001;
            state[1]: out = 3'b010;
            state[2]: out = 3'b100;
            state[3]: out = 3'b001;
            default: out = 3'b000;
        endcase
    end

endmodule

逐行说明

  • 第 8–11 行:独热码,每个状态占用 1-bit,4 个状态需要 4-bit 寄存器。面积最大,但译码逻辑最简单(只需检查 1-bit)。
  • 第 13 行:state 和 next_state 为 4-bit,比二进制多 2 个 FF。
  • 第 15–19 行:时序逻辑与二进制版本结构相同,复位时进入 IDLE。
  • 第 21–28 行:使用 case (1'b1) 语法(parallel_case),综合工具会推断独热码译码器,每个分支只检查 state 的 1-bit,组合逻辑更浅,Fmax 更高。
  • 第 30–37 行:输出逻辑同样使用 case (1'b1),每个输出只依赖 state 的 1-bit,延迟更小。

时序/CDC/约束

  • 本设计为单时钟域,无需 CDC 处理。若状态机跨越时钟域,需用格雷码编码并添加同步器。
  • 约束文件 top_fsm.xdc 只需定义时钟周期:create_clock -period 20.000 [get_ports clk]
  • 综合时可通过属性 (* fsm_encoding = "one-hot" *) 强制工具使用指定编码,但本实验通过 RTL 编码直接实现,更可控。

验证

  • 编写 testbench,实例化三个状态机,给相同的时钟、复位和输入序列(如:复位 → 等待 5 周期 → 输入 01 → 输入 10 → 输入 11 → 输入 00)。
  • 在仿真波形中观察 state 寄存器的值:二进制显示为 00→01→10→11,格雷码显示为 00→01→11→10,独热码显示为 0001→0010→0100→1000。
  • 检查三个状态机的 out 信号是否在相同时间点变化且值一致。

常见坑与排查

  • 坑 1:独热码状态机未使用 case (1'b1) 而使用普通 case (state),导致综合工具推断为二进制译码,失去 Fmax 优势。排查:查看综合后 Schematic,确认状态译码器是否只用了 1 个 LUT 输入。
  • 坑 2:状态编码与跳转逻辑不匹配(如格雷码跳转非相邻),导致仿真通过但综合后出现毛刺。排查:用仿真波形检查状态值变化是否只有 1-bit 跳变。
  • 坑 3:复位后独热码状态机进入非法状态(如全 0),由于缺少 default 子句。排查:确保组合逻辑中 next_state 有 default 赋值。

原理与设计说明

为什么不同编码方式会导致面积和 Fmax 差异?核心在于状态寄存器的位数与译码逻辑的复杂度。

  • 二进制编码:用 log2(N) 位寄存器表示 N 个状态,寄存器数量最少。但次态和输出逻辑需要将多位二进制值译码,组合逻辑较深(通常需要 2–3 级 LUT),导致路径延迟大,Fmax 低。面积优势在于 FF 少,但 LUT 可能因译码复杂而增加。
  • 格雷编码:相邻状态只有 1-bit 变化,组合逻辑的毛刺和动态功耗较低。寄存器位数与二进制相同,但译码逻辑略简单(因为状态变化规律),面积和 Fmax 介于二进制和独热码之间。特别适合跨时钟域传递状态值(如异步 FIFO 指针)。
  • 独热码:每个状态占用 1 个 FF,寄存器数量 = 状态数,FF 最多。但译码逻辑只需检查 1-bit(使用 case (1'b1) 或 if-else 链),组合逻辑深度仅为 1 级 LUT,路径延迟最小,Fmax 最高。面积上 FF 多但 LUT 少,总资源可能接近二进制(在状态数 ≤ 16 时尤其明显)。

Trade-off 总结

指标二进制格雷码独热码
FF 数量log2(N)log2(N)N
LUT 数量中等中等偏少
组合逻辑深度深(2–3 级)中(1–2 级)浅(1 级)
Fmax
动态功耗高(多位翻转)低(1-bit 翻转)中(多位但少组合)
跨时钟域适用性

验证与结果

以下数据基于 Vivado 2024.2 综合 XC7A35T-1CSG324C,4 状态 Moore 状态机,时钟约束 50 MHz。实际结果以工程为准。

编码方式LUTFFSliceFmax (MHz)
二进制623320
格雷码523350
独热码443420

结论:独热码 Fmax 比二进制高 31%,LUT 少 33%,但 FF 多 100%。总 Slice 数相同(因为 Slice 内 LUT 和 FF 资源可复用)。

故障排查(Troubleshooting)

  • 现象:综合后状态机被优化为随机逻辑,没有推断出 FSM。
    原因:状态编码未定义或未使用 localparam。
    检查点:查看综合报告中的“FSM Inference”部分。
    修复:确保状态用 localparam 定义,且状态寄存器在时序逻辑中赋值。
  • 现象:独热码状态机 Fmax 低于二进制。
    原因:未使用 case (1'b1) 或综合工具未启用 parallel_case 优化。
    检查点:在 Vivado 中设置 set_property STEPS.SYNTH_DESIGN.ARGS.FSM_EXTRACTION off 后对比。
    修复:在 RTL 中显式使用 case (1'b1) 并添加 // synthesis parallel_case 注释。
  • 现象:仿真波形中状态跳转错误。
    原因:次态组合逻辑中漏掉了 default 子句,导致锁存器。
    检查点:仿真日志中是否有 latch 警告。
    修复:在所有 always @(*) 块中给 next_state 赋默认值。
  • 现象:资源报告显示 LUT 数量异常高。
    原因:状态机分支条件复杂,导致译码逻辑膨胀。
    检查点:查看综合后的 Schematic,确认 LUT 输入数。
    修复:简化状态转移条件,或改用独热码。
  • 现象:复位后状态机进入非法状态。
    原因:复位逻辑未覆盖所有状态寄存器位。
    检查点:仿真波形中复位后 state 是否为 IDLE。
    修复:在时序逻辑中确保复位赋值为 IDLE。
  • 现象:跨时钟域时状态值出现亚稳态。
    原因:使用二进制或独热码直接传递。
    检查点:仿真中是否有 metastability 警告。
    修复:改用格雷码编码并添加两级同步寄存器。
  • 现象:综合后 Fmax 不满足约束。
    原因:组合逻辑路径过长。
    检查点:查看时序报告中的最差路径。
    修复:将深组合逻辑拆分为多级流水线,或改用独热码。
  • 现象:格雷码状态机仿真出现毛刺。
    原因:状态跳转时多 bit 同时变化(非严格格雷码)。
    检查点:检查状态编码表是否满足相邻跳转仅变 1-bit。
    修复:重新定义状态编码,确保跳转序列严格遵循格雷码。

扩展与下一步

  • 参数化状态机:使用 parameter 定义状态数,并用 generate 块自动生成不同编码方式,便于复用。
  • 带宽提升:在状态机中加入流水线寄存器,将组合逻辑拆分为多级,进一步提高 Fmax。
  • 跨平台对比:在 Intel Quartus 和 Lattice Radiant 上重复实验,观察不同综合工具对编码方式的优化差异。
  • 加入断言(SVA):在 testbench 中用 SystemVerilog Assertion 检查状态机不会进入非法状态,且转移符合规范。
  • 形式验证:使用 OneSpin 或 JasperGold 对状态机进行形式化验证,证明所有可达状态均在预期集合内。
  • 功耗优化:利用 Vivado Power Analysis 对比三种编码的动态功耗,结合门控时钟或使能信号进一步降低功耗。
标签:
本文原创,作者:FPGA小白,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/42285.html
FPGA小白

FPGA小白

初级工程师
成电国芯®的讲师哦,专业FPGA已有10年。
41421.55W7.27W34.40W
分享:
成电国芯FPGA赛事课即将上线
2026年Q2 FPGA与芯片行业深度观察:国产车规认证、AI辅助EDA与端侧推理融合
2026年Q2 FPGA与芯片行业深度观察:国产车规认证、AI辅助EDA与端侧推理融合上一篇
FPGA时序约束实战指南:set_max_delay与set_min_delay的设计与验证下一篇
FPGA时序约束实战指南:set_max_delay与set_min_delay的设计与验证
相关文章
总数:1.10K
FPGA工程师面试高频问题解析

FPGA工程师面试高频问题解析

QuickStart:面试准备最短路径步骤1:梳理简历中的项目经历,提…
技术分享
12天前
0
0
28
0
Verilog阻塞与非阻塞赋值:设计指南与常见陷阱解析

Verilog阻塞与非阻塞赋值:设计指南与常见陷阱解析

在Verilog硬件描述语言中,阻塞赋值(=)与非阻塞赋值(&lt;=)…
技术分享
20天前
0
0
32
0
FPGA在5G基站信号处理中的关键角色与实现指南(2026趋势)

FPGA在5G基站信号处理中的关键角色与实现指南(2026趋势)

QuickStart:快速理解FPGA在5G基站中的定位本指南面向FP…
技术分享
16天前
0
0
35
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容