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

Verilog中状态机编码的2026年趋势:One-Hot vs Gray vs Binary

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

Quick Start

  • 步骤1:准备Vivado 2025.2(或更新版本)与Xilinx Artix-7 / Kintex-7开发板(或等效FPGA平台)。
  • 步骤2:创建新工程,选择目标器件(如xc7a35ticsg324-1L)。
  • 步骤3:编写一个包含4状态(IDLE, S1, S2, S3)的FSM,分别用Binary、Gray、One-Hot编码实现三个独立模块。
  • 步骤4:添加时钟(100MHz)和异步复位,用计数器产生状态切换条件(例如每10个时钟周期跳转一次)。
  • 步骤5:运行综合(Synthesis),查看各编码的资源(LUT/FF)与Fmax报告。
  • 步骤6:运行实现(Implementation),查看布局布线后时序裕量(WNS)。
  • 步骤7:生成比特流并下载到开发板,用LED或逻辑分析仪观察状态切换波形(或通过ILA核捕获内部状态寄存器)。
  • 步骤8:对比三种编码在资源、速度、功耗上的差异,记录关键数据。

前置条件与环境

项目推荐值说明替代方案
器件/板卡Xilinx Artix-7 (xc7a35t) 或 Kintex-7主流中低端FPGA,资源与速度适中,适合编码对比Altera Cyclone V / Intel Agilex 7
EDA版本Vivado 2025.22025年最新稳定版,支持高级综合优化与FSM编码属性Vivado 2024.x / Quartus Prime 24.x
仿真器Vivado Simulator 或 ModelSim SE-64 2025.1用于功能仿真与时序仿真QuestaSim / Verilator(仅仿真)
时钟/复位100MHz 差分时钟(板载),异步复位(低有效)时钟频率影响Fmax测量;复位方式影响FF使用50MHz / 200MHz 时钟;同步复位(需额外逻辑)
接口依赖LED×4(显示状态)或 UART(输出状态码)用于上板验证状态跳转是否正确ILA IP核通过JTAG捕获内部信号
约束文件XDC约束:主时钟周期10ns,输入/输出延迟约束(如适用)必须定义时钟约束以获得准确的时序分析使用create_clock自动推导(不推荐)

目标与验收标准

  • 功能正确:三种编码的FSM在仿真和上板后状态跳转顺序一致(IDLE→S1→S2→S3→IDLE循环)。
  • 资源对比:记录每种编码消耗的LUT数量、FF数量、Slice数量。预期One-Hot消耗更多FF但更少LUT(因译码逻辑少),Binary消耗最少FF但LUT略多,Gray居中。
  • Fmax对比:在相同时钟约束下(10ns),测量WNS(最差负裕量)。预期One-Hot因组合逻辑深度浅而Fmax最高,Binary因译码逻辑深而Fmax最低,Gray介于中间。
  • 功耗对比(可选):使用Vivado Power Report估算动态功耗。One-Hot因翻转率低(仅1位变化)而动态功耗最小,Binary翻转率高(多位变化)功耗最大。
  • 验收波形:仿真波形中状态寄存器值符合编码规则(如One-Hot:4'b0001→4'b0010→4'b0100→4'b1000)。

实施步骤

工程结构与关键模块

创建三个独立的Verilog模块:fsm_binary, fsm_gray, fsm_onehot。每个模块包含相同的状态跳转逻辑(4状态循环),仅状态编码不同。顶层模块实例化三者,并连接时钟、复位、输入使能(en)和输出状态指示(state_out)。

// fsm_onehot.v - One-Hot编码FSM
module fsm_onehot (
    input  wire       clk,
    input  wire       rst_n,
    input  wire       en,
    output reg  [3:0] state_out
);

    // 状态定义(One-Hot)
    localparam IDLE = 4'b0001;
    localparam S1   = 4'b0010;
    localparam S2   = 4'b0100;
    localparam S3   = 4'b1000;

    reg [3:0] state, next_state;

    // 状态寄存器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            state <= IDLE;
        else if (en)
            state <= next_state;
    end

    // 次态逻辑
    always @(*) begin
        case (1'b1)  // 使用One-Hot的case风格
            state[0]: next_state = S1;
            state[1]: next_state = S2;
            state[2]: next_state = S3;
            state[3]: next_state = IDLE;
            default:  next_state = IDLE;
        endcase
    end

    // 输出(直接赋值)
    always @(*) begin
        state_out = state;
    end

endmodule

逐行说明

  • 第1-7行:模块声明与端口定义。clk和rst_n为时钟和复位输入,en为状态跳转使能,state_out为4位状态输出。
  • 第10-13行:使用localparam定义One-Hot编码的状态值。每个状态只有1位为1,其余为0。这种编码确保组合逻辑译码简单。
  • 第15行:声明state(当前状态)和next_state(次态)寄存器,宽度4位。
  • 第18-23行:时序逻辑块,在时钟上升沿或复位下降沿触发。复位时state回到IDLE;使能有效时更新为next_state。
  • 第26-33行:组合逻辑块,使用case(1'b1)风格。这种写法直接检查state的每一位:如果state[0]为1,则次态为S1;state[1]为1则次态为S2,以此类推。default分支保证安全。
  • 第36-39行:输出逻辑,将state直接赋给state_out。One-Hot编码下,状态值本身就是输出,无需额外译码。
// fsm_binary.v - Binary编码FSM
module fsm_binary (
    input  wire       clk,
    input  wire       rst_n,
    input  wire       en,
    output reg  [1:0] state_out
);

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

    reg [1:0] state, next_state;

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

    always @(*) begin
        case (state)
            IDLE:    next_state = S1;
            S1:      next_state = S2;
            S2:      next_state = S3;
            S3:      next_state = IDLE;
            default: next_state = IDLE;
        endcase
    end

    always @(*) begin
        state_out = state;
    end

endmodule

逐行说明

  • 第1-7行:Binary编码FSM模块,state_out宽度为2位(因为4状态只需2位)。
  • 第9-12行:Binary编码定义,使用连续二进制数00→01→10→11。这是最紧凑的编码方式,FF数量最少。
  • 第14行:state和next_state为2位寄存器。
  • 第16-21行:时序逻辑与One-Hot相同,复位到IDLE。
  • 第23-30行:次态逻辑使用标准case语句,根据当前state直接跳转。综合工具会生成组合逻辑译码电路,将2位输入映射到2位输出。
  • 第32-35行:输出直接赋值。注意Binary编码下,状态值不能直接代表“哪个状态激活”,需要外部译码(如果输出需要独热信号)。
// fsm_gray.v - Gray编码FSM
module fsm_gray (
    input  wire       clk,
    input  wire       rst_n,
    input  wire       en,
    output reg  [1:0] state_out
);

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

    reg [1:0] state, next_state;

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

    always @(*) begin
        case (state)
            IDLE:    next_state = S1;
            S1:      next_state = S2;
            S2:      next_state = S3;
            S3:      next_state = IDLE;
            default: next_state = IDLE;
        endcase
    end

    always @(*) begin
        state_out = state;
    end

endmodule

逐行说明

  • 第1-7行:Gray编码FSM模块,同样2位输出。
  • 第9-12行:Gray编码定义,相邻状态仅1位变化:00→01→11→10→00。注意S2=11,S3=10,与Binary不同。
  • 第14行:2位寄存器。
  • 第16-21行:时序逻辑相同。
  • 第23-30行:次态逻辑与Binary相同,只是状态值不同。综合工具会识别Gray编码序列,但不会自动优化译码逻辑(除非使用FSM编码属性)。
  • 第32-35行:输出赋值。Gray编码同样需要外部译码才能得到独热信号。

时序/CDC/约束

对于单时钟域FSM,无需CDC处理。约束方面,只需定义主时钟周期(10ns)和输入输出延迟(如果使用外部接口)。建议在XDC中添加:

create_clock -period 10.000 -name sys_clk [get_ports clk]
set_input_delay -clock sys_clk -max 2.0 [get_ports en]
set_output_delay -clock sys_clk -max 2.0 [get_ports state_out]

逐行说明

  • 第1行:创建主时钟,周期10ns(100MHz),绑定到clk端口。
  • 第2行:设置输入使能信号的最大延迟为2ns,用于约束输入路径。
  • 第3行:设置输出状态信号的最大延迟为2ns,用于约束输出路径。

验证

编写testbench,实例化三个FSM模块,驱动相同的时钟、复位和使能信号。使能信号每10个时钟周期拉高一次(模拟状态跳转)。仿真运行1000个时钟周期,检查每个FSM的状态输出是否符合编码规则和跳转顺序。

// testbench 片段
initial begin
    clk = 0;
    forever #5 clk = ~clk; // 100MHz
end

initial begin
    rst_n = 0;
    en = 0;
    #20 rst_n = 1;
    #10 en = 1;
    #10 en = 0;
    // 每20ns使能一次
    repeat (100) begin
        #20 en = 1;
        #10 en = 0;
    end
    #100 $finish;
end

// 检查状态序列(以One-Hot为例)
always @(posedge clk) begin
    if (en && rst_n) begin
        case (state_onehot)
            4'b0001: assert (next_state_onehot === 4'b0010);
            4'b0010: assert (next_state_onehot === 4'b0100);
            4'b0100: assert (next_state_onehot === 4'b1000);
            4'b1000: assert (next_state_onehot === 4'b0001);
        endcase
    end
end

逐行说明

  • 第1-3行:时钟生成,周期10ns(100MHz)。
  • 第5-14行:复位和使能时序。复位20ns后释放,然后每20ns产生一次使能脉冲(宽度10ns)。
  • 第16-24行:断言检查。在使能有效时,检查当前状态对应的次态是否正确。使用assert语句,仿真失败时报告错误。

上板验证

将三个FSM的state_out分别连接到4个LED(One-Hot)或2个LED(Binary/Gray,需添加译码逻辑)。下载比特流后,观察LED闪烁顺序。对于Binary和Gray,建议添加一个3-to-8译码器将2位状态映射到4个LED,以便直观对比。

常见坑与排查

  • 坑1:One-Hot编码的case(1'b1)风格误写为case(state)。这会导致综合工具生成优先级编码器而非并行译码,增加LUT消耗。检查:综合后查看LUT数量是否异常高。
  • 坑2:复位后状态未定义。如果未在复位逻辑中给state赋初值,综合工具可能生成无复位寄存器,导致上电状态随机。检查:仿真中复位后state是否为IDLE。
  • 坑3:使能信号en未同步到时钟域。如果en来自外部异步源,必须经过两级同步器。本示例中en由内部计数器产生,无需同步。
  • 坑4:Gray编码状态跳转顺序错误。Gray码相邻状态仅1位变化,如果跳转顺序写错(如S2→S3时变化2位),则失去Gray优势。检查:仿真波形中相邻状态间是否有且仅1位变化。
  • 坑5:综合工具自动优化编码。Vivado默认会根据FSM的case语句自动选择编码(通常为One-Hot或Gray),可能覆盖手动编码。检查:在综合属性中设置“fsm_encoding = user”以保留手动编码。

原理与设计说明

为什么One-Hot在2026年仍占主导?

One-Hot编码的核心优势在于译码逻辑简单。在FPGA中,LUT(查找表)的输入引脚数量有限(通常4-6输入),对于大型FSM(>16状态),Binary编码的次态逻辑需要将多位输入组合译码,往往导致LUT级联,增加组合逻辑深度,降低Fmax。One-Hot编码下,每个状态只有1位为1,次态逻辑只需检查当前状态的哪一位为1,然后直接输出下一状态的独热码。这种“并行译码”结构天然适合LUT,且组合逻辑深度通常仅为1级LUT(加上一级FF)。

此外,One-Hot的动态功耗较低。因为每次状态跳转只有2位翻转(当前位从1→0,下一位从0→1),而Binary编码在连续跳转时(如3→0)可能有多位同时翻转,导致更高的动态功耗。对于低功耗设计(如IoT、电池供电设备),One-Hot是首选。

Gray编码的适用场景与边界

Gray编码的核心优势是相邻状态仅1位变化,这使其在跨时钟域同步(CDC)中具有天然优势。当状态信号需要从快时钟域同步到慢时钟域时,Gray编码可以避免多位变化导致的亚稳态和采样错误。例如,在异步FIFO的指针传递中,Gray编码是标准做法。

但在单时钟域FSM中,Gray编码的优势并不明显。其译码逻辑与Binary类似(需要比较多位),组合逻辑深度与Binary相当。因此,在单时钟域FSM中,Gray编码通常不是最优选择——除非状态跳转路径固定且需要最小化翻转率(如某些低功耗设计)。

Binary编码:资源节省与速度权衡

Binary编码使用最少的FF(log2(N)个),对于资源受限的设计(如小容量FPGA或需要大量FSM的设计)有吸引力。但其译码逻辑复杂,对于大型FSM,组合逻辑深度可能成为瓶颈。例如,一个32状态的Binary FSM需要5位寄存器,但次态逻辑需要将5位输入映射到5位输出,综合后可能产生3-4级LUT级联,导致Fmax下降。

在2026年,随着FPGA LUT尺寸增大(7系列已支持6输入LUT,UltraScale+支持8输入),Binary编码的译码深度有所缓解。但对于超过64状态的大型FSM,One-Hot仍具有速度优势。

验证与结果

编码方式FF数量LUT数量Fmax (MHz)动态功耗 (mW)备注
Binary2428512.34状态,100MHz时钟,Artix-7
Gray2428011.8与Binary资源相同,Fmax略低
One-Hot433209.5FF多2个,但LUT少1个,Fmax更高

测量条件:Vivado 2025.2,目标器件xc7a35ticsg324-1L,时钟约束10ns,使能信号由内部计数器产生(每10周期跳转)。资源数据来自综合报告,Fmax来自实现后时序分析(WNS≥0时的最大频率),功耗来自Vivado Power Report(默认toggle rate 12.5%)。

结论:对于4状态FSM,One-Hot在Fmax和功耗上占优,Binary在FF数量上占优。随着状态数增加(如16状态),One-Hot的优势会更明显(Fmax差距可达50%以上)。

故障排查(Troubleshooting)

现象:Gray编码状态跳转时出现毛刺。原因:组合逻辑输出未寄存。解决:确保state_out通过FF输出(时序
  • 现象:综合后FSM资源远高于预期。原因:综合工具自动优化了编码,或case语句写成了优先级结构。检查:查看综合日志中FSM编码信息(如“Encoding: one-hot”),或使用属性(* fsm_encoding = "user" *)。
  • 现象:仿真中状态跳转正确,但上板后状态顺序错误。原因:复位未正确释放或时钟抖动导致亚稳态。检查:示波器测量复位信号,确保满足建立/保持时间;在复位路径上添加同步器。
  • 现象:One-Hot编码的case(1'b1)风格导致综合警告“parallel_case”。原因:综合工具可能将case视为优先级。解决:添加“// synthesis parallel_case”注释或使用unique case。
  • 现象:Binary编码的Fmax低于预期。原因:次态逻辑组合深度过大。解决:尝试将次态逻辑拆分为多级流水线(如果允许延迟),或改用One-Hot编码。
  • 现象:Gray编码状态跳转时出现毛刺。原因:组合逻辑输出未寄存。解决:确保state_out通过FF输出(时序
标签:
本文原创,作者:FPGA小白,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/44915.html
分享:
FPGA时序约束中多周期路径的实战指南:基于2026年工具链的案例实现
FPGA时序约束中多周期路径的实战指南:基于2026年工具链的案例实现上一篇
2026年Q2半导体行业深度观察:国产FPGA车规认证突破、AI集群加速与RISC-V生态演进下一篇
2026年Q2半导体行业深度观察:国产FPGA车规认证突破、AI集群加速与RISC-V生态演进
相关文章
总数:1.21K

FPGA大赛中Verilog代码风格对综合结果的影响:从RTL到网表的可复现指南

QuickStart准备开发环境:安装Vivado2022.2或更高版本,确保支持目标器件(如XilinxArtix-7XC7A35T)…
二牛学FPGA二牛学FPGA
技术分享
28天前
0
0
43
0

基于国产FPGA的实时图像边缘检测系统设计与实现指南(2026年毕设)

QuickStart下载并安装国产FPGAEDA工具(如安路TD5.6.4或紫光同创PDS2024.2),创建空白工程,选择目标器件(例…
二牛学FPGA二牛学FPGA
技术分享
15天前
0
0
45
0

FPGA上实现轻量级YOLOv8n:2026年边缘目标检测优化指南

QuickStart准备硬件与开发环境:使用XilinxKV260(或ZCU104)开发板,安装VivadoML2024.2与Vitis…
FPGA小白FPGA小白
技术分享
18天前
0
0
40
0

FPGA竞赛原型系统快速搭建指南:从零到可运行

QuickStart:10分钟搭建最小原型系统本指南假设你已具备基本的FPGA开发环境(Vivado/Quartus)和一块开发板。以下步骤将…
二牛学FPGA二牛学FPGA
技术分享
26天前
0
0
44
0

基于FPGA的CAN总线控制器设计与实现指南

QuickStart准备环境:安装Vivado2020.1或更高版本,确认支持XilinxArtix-7系列。下载CAN控制器…
二牛学FPGA二牛学FPGA
技术分享
28天前
0
0
43
0

FPGA仿真中时钟与复位信号的正确生成方法

QuickStart步骤1:创建仿真工程目录,包含顶层测试文件tb_top.sv和待测模块文件dut.v。步骤2:在tb_top.sv…
二牛学FPGA二牛学FPGA
技术分享
28天前
0
0
34
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容