第一章:状态机基础回顾

各位同学好,我是你们的老朋友。今天咱们聊聊状态机——这个在图像加速设计里绕不开的核心话题。说实话,我做了十几年芯片设计,状态机几乎天天打交道。你想想看,从简单的像素流水线控制,到复杂的多级缓存调度,背后都离不开它。

1.1 Moore机与Mealy机:到底差在哪?

先问大家一个问题:输出到底跟谁有关?这就是区分Moore机和Mealy机的关键。

  • Moore机:输出只取决于当前状态。说白了,状态变了,输出才变。
  • Mealy机:输出不仅看当前状态,还看输入。输入一变,输出可能立刻跟着变。

我在项目中遇到过这样一个场景:做图像边缘检测加速器时,需要根据像素梯度值决定是否输出边缘标记。如果用Moore机,得等下一个时钟周期才能输出结果,延迟多了一拍。后来换成Mealy机,输入梯度值的同时就能判断输出,时序上省了一拍。嗯,这里要注意——Mealy机虽然快,但组合逻辑输出容易产生毛刺,后面我会讲怎么处理。

核心区别一句话总结:

Moore机是“状态决定输出”,Mealy机是“状态+输入决定输出”。

1.2 状态编码方式:三种套路,各有千秋

状态编码这事儿,我刚开始做设计时也纠结过。二进制、格雷码、独热码,到底选哪个?其实没有的好坏,关键看你的设计目标。

编码方式 状态数N所需触发器数 优点 缺点 典型应用场景
二进制 log₂(N) 最省触发器 状态跳转时多位同时变化,易产生毛刺 状态数较多的控制逻辑
格雷码 log₂(N) 相邻状态只有1位变化,抗干扰强 译码逻辑稍复杂 跨时钟域同步、计数器
独热码 N 译码最快,适合FPGA 触发器用量大 状态数少(通常≤16)的高速设计

我个人习惯是:状态数少于8个,用独热码,因为FPGA的查找表结构对独热码特别友好,译码几乎零延迟。状态数超过16个,用二进制,省触发器才是王道。至于格雷码,我一般在跨时钟域同步状态机时才会用——比如图像数据从像素时钟域同步到系统时钟域,格雷码能有效减少亚稳态风险。

我的小技巧:写状态机时,先用`localparam`把状态名和编码值定义清楚。这样后期改编码方式,只需要改`localparam`那一行,不用动逻辑代码。

1.3 三段式状态机写法:我的最爱

说到写法,我强烈推荐三段式。为什么?因为我在早期项目中吃过亏——用一段式写了个复杂的图像缩放状态机,结果调试时状态跳转和输出逻辑混在一起,改一个地方崩一片。从那以后,我再也没用过一段式。

三段式结构清晰,说白了就是:

  • 第一段:时序逻辑,描述状态跳转(always @(posedge clk))
  • 第二段:组合逻辑,描述下一状态(always @(*))
  • 第三段:时序或组合逻辑,描述输出(根据Moore/Mealy选择)

来看个实际例子——一个简单的图像像素计数器状态机:

// 三段式Moore状态机示例
module pixel_fsm (
    input  wire       clk,
    input  wire       rst_n,
    input  wire       pixel_valid,
    output reg  [1:0] pixel_cnt
);

    // 状态编码:独热码
    localparam IDLE  = 2'b01;
    localparam COUNT = 2'b10;

    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
        case (state)
            IDLE: begin
                if (pixel_valid)
                    next_state = COUNT;
                else
                    next_state = IDLE;
            end
            COUNT: begin
                if (pixel_valid)
                    next_state = COUNT;
                else
                    next_state = IDLE;
            end
            default: next_state = IDLE;
        endcase
    end

    // 第三段:输出逻辑(Moore型,只与state有关)
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            pixel_cnt <= 2'b00;
        else begin
            case (state)
                IDLE:  pixel_cnt <= 2'b00;
                COUNT: pixel_cnt <= pixel_cnt + 1'b1;
                default: pixel_cnt <= 2'b00;
            endcase
        end
    end

endmodule

避坑指南:我曾经在第三段输出逻辑里用了组合逻辑,结果仿真时输出波形全是毛刺。后来改成时序逻辑输出,问题立刻解决。记住:Moore机的输出建议用时序逻辑打一拍,虽然多一个时钟延迟,但信号干净多了。

1.4 本章知识体系总览

下面这张图是我自己画的,把本章的核心知识点串了起来。你一看就明白:状态机设计无非就是“选类型、定编码、写三段”。

状态机基础回顾 · 知识体系 1. 状态机类型 Moore机 Mealy机 输出只与状态有关 输出与状态+输入有关 2. 状态编码方式 二进制 格雷码 独热码 最省触发器 抗干扰强 译码最快 3. 三段式写法 第一段 第二段 第三段 状态跳转 下一状态 输出逻辑 核心原则:先选类型 → 再定编码 → 最后用三段式实现 状态数少用独热码,状态数多用二进制,跨时钟域用格雷码

嗯,以上就是状态机基础的全部内容。别小看这些基础,我见过不少工程师在复杂图像加速设计里翻车,追根溯源都是状态机没写好。下一章咱们会深入图像处理中的状态机实战,到时候这些基础都会用上。

本章要点速记:

  • Moore机输出只与状态有关,Mealy机输出与状态和输入都有关
  • 二进制省触发器,格雷码抗干扰,独热码译码快
  • 三段式写法:状态跳转 + 下一状态 + 输出逻辑,各司其职
  • Moore机输出建议用时序逻辑,避免毛刺

可可精品资料库