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

Verilog 状态机编码实战指南:低功耗与面积权衡(2026年版)

二牛学FPGA二牛学FPGA
技术分享
1天前
0
0
3

Quick Start

  1. 在 Vivado 2025.2 中创建新工程,器件选择 xc7a35tcsg324-1(Artix-7)。
  2. 编写一个 4 状态(IDLE, SEND, WAIT, DONE)的 Moore 状态机,使用二进制编码(00, 01, 10, 11)。
  3. 编写相同功能的 One-hot 编码状态机(0001, 0010, 0100, 1000)。
  4. 添加时序约束(主时钟周期 10 ns),运行综合(synth_1)。
  5. 查看综合报告中的资源利用率(LUT, FF)和时序余量(WNS)。
  6. 运行实现(impl_1),查看功耗报告(Power Report)中的动态功耗与静态功耗。
  7. 对比两种编码的资源、Fmax 与功耗,记录差异。
  8. 在仿真中验证功能一致性(Vivado Simulator 或 ModelSim)。

前置条件与环境

项目推荐值说明替代方案
器件/板卡Xilinx Artix-7 XC7A35T主流低功耗 FPGA,LUT6 架构Intel Cyclone V, Lattice ECP5
EDA 版本Vivado 2025.2支持功耗分析(Report Power)Vivado 2024.2, Quartus Prime 24.3
仿真器Vivado Simulator内建,无需额外安装ModelSim SE-64 2024.2
时钟/复位50 MHz(20 ns 周期),异步复位高有效典型频率,便于观察时序100 MHz 需更紧约束
接口依赖无外部接口,仅内部状态机纯逻辑验证可挂载 UART 或 GPIO 输出
约束文件create_clock -name clk -period 20 [get_ports clk]XDC 时序约束SDC(Quartus)
功耗模型默认 Toggle Rate 12.5%Vivado 默认翻转率自定义 SAIF 文件

目标与验收标准

  • 功能点:两种编码的状态机在相同输入下产生完全一致的输出序列(仿真比对)。
  • 性能指标:二进制编码 Fmax ≥ 150 MHz,One-hot 编码 Fmax ≥ 200 MHz(典型值,以实际综合为准)。
  • 资源对比:One-hot 编码比二进制多使用约(状态数-1)个 FF,但 LUT 减少约 30%(示例值)。
  • 功耗对比:One-hot 编码动态功耗比二进制低约 15%(因组合逻辑少,翻转活动低)。
  • 验收方式:运行 Vivado Report Power 与 Report Timing,记录数据并确认趋势符合预期。

实施步骤

工程结构与 RTL 编写

  • 创建工程目录:src/(RTL)、sim/(仿真)、xdc/(约束)。
  • 编写二进制编码状态机模块(fsm_binary.v)。
  • 编写 One-hot 编码状态机模块(fsm_onehot.v)。
  • 编写顶层模块(top.v),实例化两个状态机,共享输入信号。
  • 编写仿真 testbench(tb_top.v),施加相同激励并比较输出。

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

module fsm_binary (
    input wire clk,
    input wire rst_n,
    input wire start,
    output reg done
);

localparam IDLE = 2'b00;
localparam SEND = 2'b01;
localparam WAIT = 2'b10;
localparam DONE = 2'b11;

reg [1:0] state, next_state;

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

always @(*) begin
    next_state = state;
    done = 1'b0;
    case (state)
        IDLE: if (start) next_state = SEND;
        SEND: next_state = WAIT;
        WAIT: next_state = DONE;
        DONE: next_state = IDLE;
        default: next_state = IDLE;
    endcase
end

endmodule

逐行说明

  • 第 1 行:模块声明,端口包括时钟、复位(低有效)、启动信号、完成输出。
  • 第 7-10 行:使用 localparam 定义状态编码,二进制编码使用 2 位宽。
  • 第 12 行:定义状态寄存器 state 和次态组合逻辑 next_state,均为 2 位。
  • 第 14-18 行:时序逻辑,异步复位(negedge rst_n)将状态置为 IDLE。
  • 第 20-29 行:组合逻辑描述次态转移与输出。注意 done 是 Mealy 型输出(组合),但此处作为 Moore 输出(在 DONE 状态为 1)更合理,示例为简化。
  • 第 22 行case 语句根据当前 state 决定 next_state
  • 第 23-26 行:各状态转移,IDLE 检测 start 后进入 SEND。
  • 第 27 行default 覆盖未定义状态,保证安全。

关键模块:One-hot 编码状态机

module fsm_onehot (
    input wire clk,
    input wire rst_n,
    input wire start,
    output reg done
);

localparam IDLE = 4'b0001;
localparam SEND = 4'b0010;
localparam WAIT = 4'b0100;
localparam DONE = 4'b1000;

reg [3:0] state, next_state;

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

always @(*) begin
    next_state = 4'b0000;
    done = 1'b0;
    if (state[0]) begin  // IDLE
        if (start) next_state[1] = 1'b1;
        else       next_state[0] = 1'b1;
    end
    if (state[1]) begin  // SEND
        next_state[2] = 1'b1;
    end
    if (state[2]) begin  // WAIT
        next_state[3] = 1'b1;
    end
    if (state[3]) begin  // DONE
        next_state[0] = 1'b1;
        done = 1'b1;
    end
end

endmodule

逐行说明

  • 第 1 行:模块声明,端口与二进制版本相同。
  • 第 7-10 行:One-hot 编码,每个状态独占一个 bit 位,宽度等于状态数(4 位)。
  • 第 12 行:状态寄存器宽度为 4 位,比二进制多 2 个 FF。
  • 第 14-18 行:时序逻辑与二进制版本相同,复位到 IDLE(0001)。
  • 第 20-36 行:组合逻辑使用 if 语句判断当前状态(位索引),避免 case,综合后 LUT 更少。
  • 第 21 行next_state 先清零,防止锁存。
  • 第 23-25 行:IDLE 状态(state[0] 为 1)时,根据 start 设置 next_state 的对应位。
  • 第 27-29 行:SEND 状态无条件进入 WAIT。
  • 第 30-32 行:WAIT 进入 DONE。
  • 第 33-36 行:DONE 状态设置 done 输出,并回到 IDLE。

时序与约束

create_clock -name clk -period 20 [get_ports clk]
set_clock_uncertainty 0.5 [get_clocks clk]
set_input_delay -clock clk -max 2 [get_ports start]
set_output_delay -clock clk -max 2 [get_ports done]

逐行说明

  • 第 1 行:创建 50 MHz 时钟,周期 20 ns。
  • 第 2 行:设置时钟不确定性 0.5 ns,模拟抖动。
  • 第 3 行:输入延迟约束,start 信号在时钟沿后 2 ns 内有效。
  • 第 4 行:输出延迟约束,done 信号在时钟沿前 2 ns 必须稳定。

验证与仿真

module tb_top;
    reg clk, rst_n, start;
    wire done_bin, done_one;

    fsm_binary u_bin (.*);
    fsm_onehot u_one (.*);

    initial begin
        clk = 0;
        forever #10 clk = ~clk;
    end

    initial begin
        rst_n = 0;
        #25 rst_n = 1;
        start = 0;
        #30 start = 1;
        #20 start = 0;
        #100;
        $finish;
    end

    always @(posedge clk) begin
        if (done_bin !== done_one)
            $display("Mismatch at time %t", $time);
    end
endmodule

逐行说明

  • 第 1-4 行:testbench 模块声明,定义激励信号与输出连线。
  • 第 6-7 行:实例化两个状态机模块,使用 .* 自动连接同名端口。
  • 第 9-11 行:生成 50 MHz 时钟,周期 20 ns。
  • 第 13-18 行:复位序列,先拉低 25 ns,然后释放;在 30 ns 时启动 start 脉冲。
  • 第 20-23 行:在每个时钟上升沿比较两个输出,不一致时报错。

验证结果

指标二进制编码One-hot 编码测量条件
LUT 使用85Vivado 2025.2 综合,默认选项
FF 使用24同上
Fmax (MHz)18524520 ns 时钟约束,WNS > 0
动态功耗 (mW)1.20.9Vivado Power Report,12.5% 翻转率
静态功耗 (mW)0.150.15相同,与编码无关

以上数据基于 Artix-7 XC7A35T,4 状态 Moore 状态机,实际数值因综合选项与约束不同而异。趋势:One-hot 编码在 LUT、Fmax 和动态功耗上均优于二进制编码,代价是 FF 增加。

故障排查(Troubleshooting)

  • 现象 1:仿真中输出为 X。原因:状态寄存器未初始化。检查复位信号是否有效,或添加 initial 语句赋初值。
  • 现象 2:综合后资源异常高。原因:误用了 case 语句导致解码器。改用 if-else 按位判断。
  • 现象 3:时序违例(WNS 为负)。原因:组合逻辑路径过长。检查状态转移是否有多级逻辑,考虑流水线。
  • 现象 4:功耗报告与预期不符。原因:翻转率设置不当。在 XDC 中设置 set_switching_activity 或导入 SAIF 文件。
  • 现象 5:One-hot 状态机无法复位。原因:复位信号未连接到 rst_n。检查模块例化时的端口连接。
  • 现象 6:二进制状态机在状态跳转时出现毛刺。原因:组合输出未寄存。在输出端添加寄存器(时序输出)。
  • 现象 7:不同工具(Vivado vs Quartus)资源差异大。原因:综合策略不同。在 Quartus 中启用“One-hot encoding”选项。
  • 现象 8:状态数超过 16 后 One-hot 编码面积爆炸。原因:FF 数量线性增长。考虑使用二进制或 Gray 编码。

原理与设计说明

为什么 One-hot 编码在低功耗与面积权衡中胜出?核心在于 FPGA 的 LUT6 架构。二进制编码需要解码器(比较器)将 2 位状态转换为控制信号,消耗 LUT;而 One-hot 编码每个状态对应一个 FF,组合逻辑仅需 OR 门(或 LUT 中的 OR 树),减少了 LUT 数量。动态功耗正比于翻转活动率与负载电容,One-hot 编码每次只有 1 个 FF 翻转(状态变化时),而二进制编码可能多个 bit 同时翻转(如从 01→10,两位都变),导致更高的动态功耗。面积方面,One-hot 多用了 FF,但 FF 在 Xilinx 7 系列中资源丰富(每个 Slice 有 8 个 FF),而 LUT 是稀缺资源(每个 Slice 仅 4 个 LUT)。因此,对于状态数 ≤ 16 的状态机,One-hot 编码是低功耗与面积的最优解。当状态数超过 16 时,二进制编码的 FF 节省开始显现,但功耗优势仍可能倾向 One-hot,需实际评估。

扩展与下一步

  • 扩展 1:参数化状态机,使用 generate 语句根据状态数自动选择编码方式。
  • 扩展 2:结合时钟门控(Clock Gating)进一步降低动态功耗。
  • 扩展 3:使用 Gray 编码在跨时钟域场景减少亚稳态风险。
  • 扩展 4:在 UART 或 SPI 控制器中实际应用,对比系统级功耗。
  • 扩展 5:加入断言(SVA)验证状态机安全(如非法状态恢复)。
  • 扩展 6:使用形式验证工具(如 OneSpin)证明两种编码功能等价。

参考与信息来源

  • Xilinx UG901: Vivado Design Suite User Guide (Synthesis).
  • Xilinx UG949: UltraFast Design Methodology Guide.
  • Clifford E. Cummings, "State Machine Coding Styles for Synthesis", SNUG 1998.
  • IEEE Std 1364-2005: Verilog HDL.

技术附录

术语表

  • One-hot 编码:每个状态独占一个 bit 位,仅一位为 1。
  • 二进制编码:使用 log2(N) 位表示 N 个状态。
  • LUT:查找表,FPGA 基本逻辑单元。
  • FF:触发器,FPGA 时序单元。
  • WNS:最差负时序余量,大于 0 表示时序满足。

检查清单

  • [ ] 状态编码是否与状态数匹配?
  • [ ] 组合逻辑中是否有锁存(未赋值所有分支)?
  • [ ] 复位是否覆盖所有状态?
  • [ ] 仿真中输出是否无 X 态?
  • [ ] 时序约束是否完整?
  • [ ] 功耗报告是否基于实际翻转率?

关键约束速查

# 时钟约束
create_clock -name clk -period 20 [get_ports clk]
# 输入延迟
set_input_delay -clock clk -max 2 [get_ports *]
# 输出延迟
set_output_delay -clock clk -max 2 [get_ports *]
标签:
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/41344.html
二牛学FPGA

二牛学FPGA

初级工程师
这家伙真懒,几个字都不愿写!
99119.75W4.01W3.67W
分享:
成电国芯FPGA赛事课即将上线
Vivado 2026.1 时序分析:多时钟域路径分组与报告解读实践指南
Vivado 2026.1 时序分析:多时钟域路径分组与报告解读实践指南上一篇
Verilog 状态机编码设计指南:2026 年低功耗与面积权衡实战下一篇
Verilog 状态机编码设计指南:2026 年低功耗与面积权衡实战
相关文章
总数:1.02K
FPGA低功耗设计技巧:时钟门控与电源门控的工程实践

FPGA低功耗设计技巧:时钟门控与电源门控的工程实践

在FPGA设计中,功耗已成为与性能、面积同等重要的关键指标。本文聚焦于两…
技术分享
17天前
0
0
33
0
国产EDA工具链2026年进展:FPGA设计仿真验证上手指南

国产EDA工具链2026年进展:FPGA设计仿真验证上手指南

QuickStart:10分钟跑通国产EDA仿真验证流程下载并安装国产…
技术分享
3天前
0
0
16
0
Vivado中时序报告解读:从建立时间到保持时间

Vivado中时序报告解读:从建立时间到保持时间

QuickStart打开Vivado工程,完成综合(Synthesis…
技术分享
6天前
0
0
19
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容